Browse Source

Repo up again.

Mihawk 10 years ago
commit
d4a3773445
11 changed files with 3426 additions and 0 deletions
  1. 283 0
      QWClient.cpp
  2. 91 0
      QWClient.h
  3. 2101 0
      QWClientPrivate.cpp
  4. 245 0
      QWClientPrivate.h
  5. 148 0
      QWPack.cpp
  6. 47 0
      QWPack.h
  7. 76 0
      QWTables.cc
  8. 49 0
      QWTables.h
  9. 310 0
      quakedef.h
  10. 45 0
      qwclient.pro
  11. 31 0
      qwclient_global.h

+ 283 - 0
QWClient.cpp

@@ -0,0 +1,283 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#include "QWClient.h"
+#include "QWClientPrivate.h"
+#include <stdio.h>
+
+QWClient::QWClient():
+  myImplementation(new QWClientPrivate(this))
+{
+}
+
+void QWClient::connect(const char *host, quint16 port)
+{
+	myImplementation->connect(host, port);
+}
+
+void QWClient::run()
+{
+	myImplementation->run();
+}
+
+void QWClient::disconnect()
+{
+  myImplementation->disconnect();
+}
+
+QWClient::~QWClient()
+{
+	delete myImplementation;
+}
+
+void QWClient::join()
+{
+	myImplementation->join();
+}
+
+void QWClient::observe()
+{
+	myImplementation->observe();
+}
+
+void QWClient::setQuakeFolder(const char *path)
+{
+	myImplementation->setQuakeFolder(path);
+}
+
+void QWClient::setColor(quint8 bottom, quint8 top)
+{
+	myImplementation->setColor(bottom, top);
+}
+
+void QWClient::setName(const char *name)
+{
+	myImplementation->setName(name);
+}
+
+void QWClient::setSpectator(bool spectate)
+{
+	myImplementation->setSpectator(spectate);
+}
+
+void QWClient::stripColor(char *string)
+{
+	QWClientPrivate::stripColor(string);
+}
+
+void QWClient::sendCmd(const char *cmd)
+{
+	QString command(cmd);
+	myImplementation->sendCmd(cmd);
+}
+
+void QWClient::setRate(quint16 rate)
+{
+	myImplementation->setRate(rate);
+}
+
+void QWClient::setPing(quint16 ping)
+{
+	myImplementation->setPing(ping);
+}
+
+void QWClient::reconnect()
+{
+	myImplementation->reconnect();
+}
+
+const char* QWClient::host() const
+{
+  static char host[255];
+  memcpy(host, myImplementation->host().toAscii().data(), myImplementation->host().toAscii().size()+1);
+  return host;
+}
+
+quint16 QWClient::port() const
+{
+	return myImplementation->port();
+}
+
+QWClient::ClientState QWClient::state() const
+{
+	return myImplementation->state();
+}
+
+const QString& QWClient::gameDir() const
+{
+  return myImplementation->gameDir();
+}
+
+const QString& QWClient::quakeDir() const
+{
+  return myImplementation->quakeDir();
+}
+
+void QWClient::setPassword(const char *password)
+{
+  myImplementation->setPassword(QString(password));
+}
+
+//========================================================================
+// Events
+
+void QWClient::onCenterPrint(const char *)
+{
+
+}
+
+void QWClient::onDamage(int, int)
+{
+
+}
+
+void QWClient::onDisconnect()
+{
+
+}
+
+void QWClient::onDownloadFinished()
+{
+
+}
+
+void QWClient::onDownloadProgress(int)
+{
+
+}
+
+void QWClient::onDownloadStarted(const char *)
+{
+
+}
+
+void QWClient::onEntGravityChange(float)
+{
+
+}
+
+void QWClient::onLevelChanged(int, const char*, float, float, float, float, float, float, float, float, float, float)
+{
+
+}
+
+void QWClient::onMaxSpeedChange(float)
+{
+
+}
+
+void QWClient::onModelListFile(const char *)
+{
+
+}
+
+void QWClient::onPlayerInfo(int, float, float, float)
+{
+
+}
+
+void QWClient::onPlaySound(int)
+{
+
+}
+
+void QWClient::onPrint(int, const char *)
+{
+
+}
+
+void QWClient::onServerInfo(const char *, const char *)
+{
+
+}
+
+void QWClient::onSetInfo(int, const char *, const char *)
+{
+
+}
+
+void QWClient::onSetPause(bool)
+{
+
+}
+
+void QWClient::onSoundListFile(const char *)
+{
+
+}
+
+void QWClient::onStuffedCmd(const char *)
+{
+
+}
+
+void QWClient::onUpdateFrags(int, int)
+{
+
+}
+
+void QWClient::onUpdatePing(int, int)
+{
+
+}
+
+void QWClient::onUpdatePL(int, int)
+{
+
+}
+
+void QWClient::onUpdateUserInfo(int, int, const char *)
+{
+
+}
+
+void QWClient::onError(const char *)
+{
+
+}
+
+void QWClient::onOOBCommand(const char *)
+{
+
+}
+
+void QWClient::onOOBEcho(const char *)
+{
+
+}
+
+void QWClient::onOOBPrint(const char *)
+{
+
+}
+
+void QWClient::onChallenge()
+{
+
+}
+
+void QWClient::onConnection()
+{
+
+}
+
+void QWClient::onConnected()
+{
+
+}

+ 91 - 0
QWClient.h

@@ -0,0 +1,91 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#ifndef QWCLIENT_H
+#define QWCLIENT_H
+
+#include "qwclient_global.h"
+
+class QWCLIENTSHARED_EXPORT QWClient {
+	friend class QWClientPrivate;
+public:
+  enum ClientState { DisconnectedState, ConnectingState, ConnectedState, LastState };
+
+	QWClient();
+	virtual ~QWClient();
+
+  void connect(const char* host, quint16 port);
+  void run();
+  void disconnect();
+	void observe();
+	void join();
+	void setQuakeFolder(const char* path);
+	void setName(const char* name);
+	void setColor(quint8 bottom, quint8 top);
+	void setSpectator(bool spectate = true);
+	void setPing(quint16 ping);
+	void setRate(quint16 rate);
+  void setPassword(const char* password);
+	void sendCmd(const char* cmd);
+  const QString& gameDir() const;
+  const QString& quakeDir() const;
+	void reconnect();
+	const char* host() const;
+	quint16 port() const;
+  ClientState state() const;
+
+	static void stripColor(char* string);
+
+protected:
+	/* Overridable functions */
+	virtual void onChallenge();
+	virtual void onConnection();
+	virtual void onConnected();
+	virtual void onModelListFile(const char* fileName);
+	virtual void onSoundListFile(const char* fileName);
+	virtual void onDownloadStarted(const char* fileName);
+	virtual void onDownloadFinished();
+	virtual void onDownloadProgress(int progress);
+	virtual void onDisconnect(); //disconnected by the remote side
+	virtual void onPrint(int level, const char* msg);
+	virtual void onCenterPrint(const char* msg);
+	virtual void onStuffedCmd(const char* cmd); //some of these commands are already handled to estabilish connection
+	virtual void onDamage(int armor, int blood);
+	virtual void onLevelChanged(int playerNum, const char* levelName, float gravity, float stopSpeed, float maxSpeed, float spectatorMaxSpeed, float accelerate, float airAccelerate, float waterAccelerate, float friction, float waterFriction, float entGravity);
+	virtual void onPlaySound(int soundNum);
+	virtual void onUpdateFrags(int playerNum, int frags);
+	virtual void onUpdatePing(int playerNum, int ping);
+	virtual void onUpdatePL(int playerNum, int pl);
+	virtual void onUpdateUserInfo(int playerNum, int userID, const char* userInfoString);
+	virtual void onSetInfo(int playerNum, const char* key, const char* value);
+	virtual void onServerInfo(const char* key, const char* value);
+	virtual void onMaxSpeedChange(float maxSpeed);
+	virtual void onEntGravityChange(float entGravity);
+	virtual void onSetPause(bool paused);
+	virtual void onPlayerInfo(int playerNum, float x, float y, float z);
+	virtual void onError(const char* description);
+	virtual void onOOBPrint(const char* msg);
+	virtual void onOOBCommand(const char* command);
+	virtual void onOOBEcho(const char* msg);
+
+private:
+	class QWClientPrivate* const myImplementation;
+};
+
+#endif // QWCLIENT_H

+ 2101 - 0
QWClientPrivate.cpp

@@ -0,0 +1,2101 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#include "QWClient.h"
+#include "QWClientPrivate.h"
+#include "QWPack.h"
+#include "QWTables.h"
+#include <QUdpSocket>
+#include <QTime>
+#include <QBuffer>
+#include <QFile>
+#include <QDir>
+#include <QRegExp>
+#include <QStringList>
+#include <QFileInfoList>
+#include <QHostInfo>
+#include <QCryptographicHash>
+#include <QCoreApplication>
+#include <QtEndian>
+#include <QDebug>
+
+const char* QWClientPrivate::ClientName		= "libqwclient";
+const char* QWClientPrivate::ClientVersion = "0.1";
+
+QWClientPrivate::QWClientPrivate(QWClient* client):
+  myClient(client),
+  mySocket(new QUdpSocket),
+  myTime(new QTime),
+  myLastServerReplyTime(new QTime),
+  myDownload(new QFile),
+  myClientName(ClientName),
+  myClientVersion(ClientVersion),
+  myState(QWClient::DisconnectedState),
+  myGameDir("qw"),
+  myQuakeDir(QCoreApplication::applicationDirPath()),
+  myPing(666),
+  myRate(3000),
+  myTopColor(0),
+  myBottomColor(0),
+  myName(ClientName),
+  mySpectatorFlag(true),
+  myTeam("lqwc")
+{
+	/* Setup IO streams */
+	myInBuffer.setBuffer(&myInData);
+	myInStream.setDevice(&myInBuffer);
+	myInStream.setByteOrder(QDataStream::LittleEndian);
+	myInBuffer.open(QIODevice::ReadOnly);
+
+	myUnreliableOutBuffer.setBuffer(&myUnreliableOutData);
+	myUnreliableOutStream.setDevice(&myUnreliableOutBuffer);
+	myUnreliableOutBuffer.open(QIODevice::WriteOnly);
+	myUnreliableOutStream.setByteOrder(QDataStream::LittleEndian);
+
+	myReliableOutBuffer.setBuffer(&myReliableOutData);
+	myReliableOutStream.setDevice(&myReliableOutBuffer);
+	myReliableOutBuffer.open(QIODevice::WriteOnly);
+	myReliableOutStream.setByteOrder(QDataStream::LittleEndian);
+
+	myOutBuffer.setBuffer(&myOutData);
+	myOutStream.setDevice(&myOutBuffer);
+	myOutBuffer.open(QIODevice::ReadWrite);
+	myOutStream.setByteOrder(QDataStream::LittleEndian);
+
+	reloadPackFiles();
+}
+
+void QWClientPrivate::setPing(quint16 ping)
+{
+	myPing = qBound<quint16>(13, ping, 999);
+}
+
+void QWClientPrivate::reconnect()
+{
+  disconnect();
+	connect(myHost.toString().toAscii().data(), myPort);
+}
+
+void QWClientPrivate::setRate(quint16 rate)
+{
+	myRate = qBound<quint16>(2500, rate, 30000);
+  if(myState != QWClient::ConnectedState)
+		return;
+
+	sendCmd("setinfo \"rate\" \"" + QString::number(rate) + "\"");
+}
+
+void QWClientPrivate::setSpectator(bool spectate)
+{
+  if(myState == QWClient::ConnectedState)
+	{
+		if(spectate && !mySpectatorFlag)
+			observe();
+		else if(!spectate && mySpectatorFlag)
+			join();
+
+		return;
+	}
+
+	mySpectatorFlag = spectate;
+}
+
+void QWClientPrivate::setPassword(const QString &password)
+{
+  myPassword = password;
+}
+
+void QWClientPrivate::setQuakeFolder(const QString &path)
+{
+	myQuakeDir = path;
+	QDir dir(myQuakeDir);
+	dir.mkpath("id1");
+	dir.mkpath(myGameDir);
+	reloadPackFiles();
+}
+
+void QWClientPrivate::join()
+{
+	mySpectatorFlag = false;
+
+  if(myState != QWClient::ConnectedState)
+		return;
+
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, "setinfo \"spectator\" \"\"");
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, "join");
+}
+
+void QWClientPrivate::observe()
+{
+	mySpectatorFlag = true;
+
+  if(myState != QWClient::ConnectedState)
+		return;
+
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, "setinfo \"spectator\" \"" + QString::number(mySpectatorFlag) + "\"");
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, "observe");
+}
+
+const QString& QWClientPrivate::gameDir() const
+{
+  return myGameDir;
+}
+
+const QString& QWClientPrivate::quakeDir() const
+{
+  return myQuakeDir;
+}
+
+void QWClientPrivate::setColor(quint8 bottom, quint8 top)
+{
+	myBottomColor = bottom;
+	myTopColor = top;
+
+  if(myState != QWClient::ConnectedState)
+		return;
+
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, "setinfo \"bottomcolor\" \"" + QString::number(myBottomColor) + "\"");
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, "setinfo \"topcolor\" \"" + QString::number(myTopColor) + "\"");
+}
+
+void QWClientPrivate::setName(const char *name)
+{
+	myName = QString(name);
+
+  if(myState == QWClient::ConnectedState)
+	{
+		writeByte(&myReliableOutStream, clc_stringcmd);
+		writeString(&myReliableOutStream, "setinfo \"name\" \"" + myName + "\"");
+	}
+}
+
+void QWClientPrivate::setTeam(const char *team)
+{
+	myTeam = QString(team);
+
+  if(myState == QWClient::ConnectedState)
+	{
+		writeByte(&myReliableOutStream, clc_stringcmd);
+		writeString(&myReliableOutStream, "setinfo \"name\" \"" + myTeam + "\"");
+	}
+}
+
+void QWClientPrivate::sendCmd(const QString &cmd)
+{
+  if(myState != QWClient::ConnectedState)
+		return;
+
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, cmd);
+}
+
+QWClientPrivate::~QWClientPrivate()
+{
+	delete myTime;
+	delete myLastServerReplyTime;
+	delete mySocket;
+	delete myDownload;
+}
+
+void QWClientPrivate::reloadPackFiles()
+{
+	for(int i = 0; i < myPacks.size(); ++i)
+		delete myPacks.at(i);
+	myPacks.clear();
+
+	QDir quakeDir(myQuakeDir);
+
+	if(!quakeDir.isReadable())
+		return;
+
+	quakeDir.cd("id1");
+
+	QFileInfoList files = quakeDir.entryInfoList(QStringList("*.pak"), QDir::Files);
+	QRegExp packRegex("pak[0-9]+\\.pak", Qt::CaseInsensitive);
+	for(int i = 0; i < files.size(); ++i)
+	{
+		if(packRegex.indexIn(files.at(i).fileName()) != -1)
+		{
+			QWPack* pack = new QWPack();
+			if(!pack->load(files.at(i).absoluteFilePath()))
+			{
+				delete pack;
+				continue;
+			}
+			myPacks.push_back(pack);
+		}
+	}
+	files.clear();
+
+	QDir gameDir(myQuakeDir + "/" + myGameDir);
+	if(!gameDir.isReadable())
+		return;
+	files = gameDir.entryInfoList(QStringList("*.pak"), QDir::Files);
+	for(int i = 0; i < files.size(); ++i)
+	{
+		if(packRegex.indexIn(files.at(i).fileName()) != -1)
+		{
+			QWPack* pack = new QWPack();
+			if(!pack->load(files.at(i).absoluteFilePath()))
+			{
+				delete pack;
+				continue;
+			}
+			myPacks.push_back(pack);
+		}
+	}
+}
+
+void QWClientPrivate::run()
+{
+	/* Read and parse packets sent by the server */
+	readPackets();
+
+	/* Send something to the server to keep the connection alive */
+	sendToServer();
+}
+
+void QWClientPrivate::sendToServer(bool dontWait)
+{
+	/* Check for resend */
+  if(myState == QWClient::ConnectingState)
+	{
+		if(myTime->elapsed() >= 5000)
+		{
+			sendConnectionless("getchallenge\n");
+			myClient->onChallenge();
+			myTime->restart();
+		}
+		return;
+	}
+
+	/* Go full speed when connecting or downloading a new map */
+  if(!dontWait && (myState != QWClient::ConnectedState || myDownload->isOpen() ? 12 : myPing) > myTime->elapsed())
+		return;
+
+  sendMovement(); //send some movement every frame
+
+	bool sendReliable = false;
+
+	if(myIncomingAck > myLastRealiableSeq && myIncomingAckReliableFlag != myOutgoingSeqReliableFlag)
+		sendReliable = true;
+
+	if(!myReliableData.size() && myReliableOutData.size())
+	{
+		myReliableData = myReliableOutData;
+		myOutgoingSeqReliableFlag ^= 1;
+		sendReliable = true;
+
+		myReliableOutData.clear();
+		myReliableOutStream.device()->seek(0);
+	}
+
+	/* Write packet header */
+	myOutData.clear();
+	myOutStream.device()->seek(0);
+
+	myOutStream << (myOutgoingSeq | (sendReliable << 31));
+	myOutStream << (myIncomingSeq | (myIncomingSeqReliableFlag << 31));
+	myOutStream << myQPort;
+	myOutgoingSeq++;
+
+	/* Write reliable buffer first */
+	if(sendReliable)
+	{
+		myOutData.append(myReliableData);
+		myLastRealiableSeq = myOutgoingSeq;
+	}
+
+	/* Unreliable part afterwards */
+	if(myOutData.size() + myUnreliableOutData.size() < MAX_MSGLEN && myUnreliableOutData.size())
+	{
+		myOutData.append(myUnreliableOutData);
+		myUnreliableOutData.clear();
+		myUnreliableOutStream.device()->seek(0);
+	}
+
+	/* Finally send the packet */
+  mySocket->write(myOutData);
+  mySocket->waitForBytesWritten();
+	myOutData.clear();
+	myOutStream.device()->seek(0);
+
+	myTime->restart();
+}
+
+void QWClientPrivate::readPackets()
+{
+	if(!mySocket->isOpen())
+		return;
+
+	if(!mySocket->hasPendingDatagrams())
+	{
+		if(myLastServerReplyTime->secsTo(QTime::currentTime()) >= 30)
+		{
+			myClient->onError("Client Timed Out.");
+      disconnect();
+			return;
+		}
+		return;
+	}
+
+	*myLastServerReplyTime = QTime::currentTime();
+
+	myInData.resize(mySocket->pendingDatagramSize());
+//	myInData = mySocket->readAll();
+	mySocket->readDatagram(myInData.data(), mySocket->pendingDatagramSize());
+
+	myInStream.device()->seek(0);
+
+	quint32 seq;
+	myInStream >> seq;
+
+	if(seq == 0xffffffff)
+		parseConnectionless();
+	else
+		parseServerMessage();
+}
+
+void QWClientPrivate::parseConnectionless()
+{
+	quint8 c;
+
+	c = readByte();
+	switch(c)
+	{
+		case S2C_CHALLENGE:
+		{
+			QString challenge;
+			QString connString;
+
+			challenge = readString();
+
+			connString.append("connect " + QString::number(PROTOCOL_VERSION) + " " + QString::number(myQPort) + " " + challenge);
+			connString.append(" \"\\rate\\" + QString::number(myRate));
+      if(!myPassword.isEmpty())
+        connString.append("\\password\\" + myPassword);
+			connString.append("\\msg\\1\\noaim\\1\\topcolor\\" + QString::number(myTopColor) + "\\bottomcolor\\" + QString::number(myBottomColor) + "\\w_switch\\2\\b_switch\\2\\*client\\" + myClientName);
+			connString.append(" " + myClientVersion + "\\name\\" + myName + "\\team\\" + myTeam + "\\spectator\\" + (mySpectatorFlag ? "1" : "0") + "\\pmodel\\33168\\emodel\\6967\\*z_ext\\383\"");
+			myInStream.device()->seek(0);
+			sendConnectionless(connString.toAscii());
+			myClient->onConnection();
+		}
+		break;
+
+		case S2C_CONNECTION:
+		{
+      myState = QWClient::ConnectedState;
+			writeByte(&myReliableOutStream, clc_stringcmd);
+			writeString(&myReliableOutStream, "new");
+			myClient->onConnected();
+		}
+		break;
+
+		case A2C_PRINT:
+			myClient->onOOBPrint(readString().toAscii().data());
+			break;
+
+		case A2C_CLIENT_COMMAND:
+			myClient->onOOBCommand(readString().toAscii().data());
+			break;
+
+		case A2A_PING:
+			sendConnectionless(QString(QChar(A2A_ACK)).toAscii());
+			break;
+
+		case A2A_ACK:
+//			qDebug("Ack");
+			break;
+
+		case A2A_NACK:
+//			qDebug("Nack");
+			break;
+
+		case A2A_ECHO:
+			myClient->onOOBEcho(readString().toAscii().data());
+			break;
+	}
+}
+
+bool QWClientPrivate::fileExists(const QString &filename)
+{
+	/* Search on the gamedir first */
+	if(QFile::exists(myQuakeDir + "/" + myGameDir + "/" + filename))
+		return true;
+
+	/* Search quake pak files next */
+	for(int i = 0; i < myPacks.size(); ++i)
+	{
+		if(myPacks[i]->exists(filename))
+			return true;
+	}
+
+	return false;
+}
+
+bool QWClientPrivate::readFile(const QString &filename, char **data, quint64 *len)
+{
+	/* Try disk */
+	QFile file(myQuakeDir + "/" + myGameDir + "/" + filename);
+  *len = 0;
+	if(file.open(QIODevice::ReadOnly))
+	{
+		*len = file.size();
+		*data = new char[*len];
+		file.read(*data, *len);
+		return true;
+	}
+
+	/* Try packs */
+	for(int i = 0; i < myPacks.size(); ++i)
+	{
+		QWPack* pack = myPacks.at(i);
+
+		if(!pack->exists(filename))
+			continue;
+		pack->read(filename, data, len);
+		return true;
+	}
+
+	return false;
+}
+
+//int	bitCounts[32];	/// just for protocol profiling
+void QWClientPrivate::parseDelta(entityState_t *from, entityState_t *to, int bits)
+{
+	int			i;
+	int			morebits;
+
+	// set everything to the state we are delta'ing from
+	*to = *from;
+
+	to->number = bits & 511;
+	bits &= ~511;
+
+	if(bits & U_MOREBITS)
+	{	// read in the low order bits
+		i = readByte();
+		bits |= i;
+	}
+
+	if(bits & U_FTE_EVENMORE && myFTEProtocolExtensions) {
+		morebits = readByte();
+		if (morebits & U_FTE_YETMORE)
+			morebits |= readByte() << 8;
+	} else {
+		morebits = 0;
+	}
+//	// count the bits for net profiling
+//	for (i=0 ; i<16 ; i++)
+//		if (bits&(1<<i))
+//			bitCounts[i]++;
+
+	to->flags = bits;
+
+	if(bits & U_MODEL)
+		to->modelindex = readByte();
+
+	if(bits & U_FRAME)
+		to->frame = readByte();
+
+	if(bits & U_COLORMAP)
+		to->colormap = readByte();
+
+	if(bits & U_SKIN)
+		to->skinnum = readByte();
+
+	if(bits & U_EFFECTS)
+		to->effects = readByte();
+
+	if(bits & U_ORIGIN1)
+		to->origin[0] = readCoord();
+
+	if(bits & U_ANGLE1)
+		to->angles[0] = readAngle();
+
+	if(bits & U_ORIGIN2)
+		to->origin[1] = readCoord();
+
+	if(bits & U_ANGLE2)
+		to->angles[1] = readAngle();
+
+	if(bits & U_ORIGIN3)
+		to->origin[2] = readCoord();
+
+	if(bits & U_ANGLE3)
+		to->angles[2] = readAngle();
+
+	if(bits & U_SOLID)
+	{}
+
+	if(morebits & U_FTE_TRANS && myFTEProtocolExtensions & FTE_PEXT_TRANS)
+		readByte();
+	if(morebits & U_FTE_ENTITYDBL)
+	{}
+	if(morebits & U_FTE_ENTITYDBL2)
+	{}
+	if(morebits & U_FTE_MODELDBL)
+	{}
+}
+
+//========================================================================
+// Parser
+
+quint32 QWClientPrivate::littleLong(quint32 l)
+{
+	if(QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+		return qToLittleEndian<quint32>(l);
+	else
+		return l;
+}
+
+quint16 QWClientPrivate::littleShort(quint16 s)
+{
+	if(QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+		return qToLittleEndian<quint16>(s);
+	else
+		return s;
+}
+
+float QWClientPrivate::littleFloat(float f)
+{
+	if(QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+		return qToLittleEndian<float>(f);
+	else
+		return f;
+}
+
+bool QWClientPrivate::checkForBadRead(quint8 typeSize)
+{
+	if(myInStream.device()->pos()+typeSize > myInStream.device()->size())
+	{
+		myBadReadFlag = true;
+		return true;
+	}
+	return false;
+}
+
+float QWClientPrivate::readAngle()
+{
+	if(checkForBadRead(1))
+		return -1;
+
+	quint8 angle;
+
+	myInStream >> angle;
+	return angle * (360.0f/256);
+}
+
+float QWClientPrivate::readAngle16()
+{
+	if(checkForBadRead(2))
+		return -1;
+
+	quint16 angle;
+
+	myInStream >> angle;
+	return angle * (360.0f/65536);
+}
+
+float QWClientPrivate::readCoord()
+{
+	if(checkForBadRead(2))
+		return -1;
+
+	quint16 coord;
+
+	myInStream >> coord;
+	return coord * (1.0f/8);
+}
+
+quint8 QWClientPrivate::readByte()
+{
+	if(checkForBadRead(1))
+		return 0xff;
+
+	quint8 byte;
+
+	myInStream >> byte;
+	return byte;
+}
+
+float QWClientPrivate::readFloat()
+{
+	union
+	{
+		quint8	b[4];
+		int			l;
+		float		f;
+	} d;
+
+	d.b[0] = readByte();
+	d.b[1] = readByte();
+	d.b[2] = readByte();
+	d.b[3] = readByte();
+
+	return littleFloat(d.f);
+}
+
+const QString QWClientPrivate::readString()
+{
+	QString str;
+	quint8 c;
+	c = readByte();
+	while(c && c != 255) //bad read or null terminator
+	{
+		str.append(c);
+		c = readByte();
+	}
+	return str;
+}
+
+qint32 QWClientPrivate::readLong()
+{
+	if(checkForBadRead(4))
+		return -1;
+
+	quint32 l;
+
+	myInStream >> l;
+	return l;
+}
+
+qint16 QWClientPrivate::readShort()
+{
+	if(checkForBadRead(2))
+		return -1;
+
+	qint16 s;
+
+	myInStream >> s;
+	return s;
+}
+
+//========================================================================
+
+void QWClientPrivate::parseSvcNoop()
+{
+
+}
+
+void QWClientPrivate::parseSvcDisconnect()
+{
+	mySocket->close();
+  myState = QWClient::DisconnectedState;
+	myClient->onDisconnect();
+}
+
+void QWClientPrivate::parseSvcPrint()
+{
+	quint8	level = readByte();
+	QString msg		= readString();
+	myClient->onPrint(level, msg.toAscii().data());
+}
+
+void QWClientPrivate::parseSvcCenterPrint()
+{
+	myClient->onCenterPrint(readString().toAscii().data());
+}
+
+void QWClientPrivate::parseSvcStuffText()
+{
+	QStringList commands = readString().split("\n");
+
+	for(int i = 0; i < commands.size(); ++i)
+	{
+		QString cmd = commands.at(i);
+
+		if(cmd == "reconnect" || cmd == "cmd new")
+		{
+			writeByte(&myReliableOutStream, clc_stringcmd);
+			writeString(&myReliableOutStream, "new");
+		}
+		else if(cmd == "cmd pext")
+		{
+			writeByte(&myReliableOutStream, clc_stringcmd);
+			writeString(&myReliableOutStream, "pext 0x00000000 0x00000000 0x00000000");
+		}
+		else if(cmd.startsWith("cmd spawn"))
+		{
+			writeByte(&myReliableOutStream, clc_stringcmd);
+			writeString(&myReliableOutStream, cmd.section(' ', 1));
+		}
+		else if(cmd.startsWith("cmd prespawn"))
+		{
+			writeByte(&myReliableOutStream, clc_stringcmd);
+			writeString(&myReliableOutStream, cmd.section(' ', 1));
+		}
+		else if(cmd == "skins")
+		{
+			writeByte(&myReliableOutStream, clc_stringcmd);
+			writeString(&myReliableOutStream, QString("begin " + QString::number(myServerCount)));
+		}
+		else if(cmd.startsWith("packet"))
+		{
+			QRegExp regex("\"(.+)\"");
+			int pos = regex.indexIn(cmd);
+			if(pos != -1)
+				sendConnectionless(regex.capturedTexts().at(1).toAscii());
+		}
+    myClient->onStuffedCmd(cmd.toAscii().data());
+	}
+}
+
+void QWClientPrivate::parseSvcDamage()
+{
+	int armor = readByte();
+	int blood = readByte();
+	myClient->onDamage(armor, blood);
+	readCoord();
+	readCoord();
+	readCoord();
+}
+
+void QWClientPrivate::parseSvcServerData()
+{
+	while (1) {
+		myProtocolVersion = readLong();
+		if(myProtocolVersion == PROTOCOL_VERSION_FTE || myProtocolVersion == PROTOCOL_VERSION_FTE2)
+		{
+			myFTEProtocolExtensions = readLong();
+			continue;
+		}
+		if(myProtocolVersion == PROTOCOL_VERSION)
+			break;
+	}
+
+	myServerCount = readLong();
+
+	myGameDir = readString();
+	QDir quakeDir(myQuakeDir);
+	if(!quakeDir.cd(myGameDir))
+		quakeDir.mkdir(myGameDir);
+
+	quint8 playerNum = readByte();		//playernum
+	if(playerNum & 0x80)
+	{
+		mySpectatorFlag = true;
+		playerNum &= ~0x80;
+	}
+	else
+		mySpectatorFlag = false;
+
+	QString lvlName = readString();
+	float a = readFloat();
+	float b = readFloat();
+	float c = readFloat();
+	float d = readFloat();
+	float e = readFloat();
+	float f = readFloat();
+	float g = readFloat();
+	float h = readFloat();
+	float i = readFloat();
+	float j = readFloat();
+	myClient->onLevelChanged(
+		playerNum,
+		lvlName.toAscii().data(),
+		a,b,c,d,e,f,g,h,i,j
+	);
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, QString("soundlist " + QString::number(myServerCount) + " 0"));
+
+  if(myDownload->isOpen())
+    myDownload->close();
+}
+
+void QWClientPrivate::parseSvcSetAngle()
+{
+	readAngle();
+	readAngle();
+	readAngle();
+}
+
+void QWClientPrivate::parseSvcLightStyle()
+{
+	readByte();
+	readString();
+}
+
+void QWClientPrivate::parseSvcSound()
+{
+	quint16 channel;
+
+	channel = readShort();
+	if (channel & SND_VOLUME)
+		readByte();
+	if (channel & SND_ATTENUATION)
+		readByte();
+	myClient->onPlaySound(readByte());
+	readCoord();
+	readCoord();
+	readCoord();
+}
+
+void QWClientPrivate::parseSvcStopSound()
+{
+	readShort();
+}
+
+void QWClientPrivate::parseSvcUpdateFrags()
+{
+	//printf("svc_updatefrags\n");
+	quint8 playerNum = readByte();
+	quint16 frags = readShort();
+	myClient->onUpdateFrags(playerNum, frags);
+}
+
+void QWClientPrivate::parseSvcUpdatePing()
+{
+	quint8 playerNum = readByte();
+	quint16 ping = readShort();
+	myClient->onUpdatePing(playerNum, ping);
+}
+
+void QWClientPrivate::parseSvcUpdatePL()
+{
+	quint8 playerNum = readByte();
+	quint8 pl = readByte();
+	myClient->onUpdatePL(playerNum, pl);
+}
+
+void QWClientPrivate::parseSvcUpdateEnterTime()
+{
+	readByte();
+	readFloat();
+}
+
+void QWClientPrivate::parseSvcSpawnBaseLine()
+{
+	//printf("svc_spawnbaseline\n");
+	readShort();
+	readByte();
+	readByte();
+	readByte();
+	readByte();
+	for(int i = 0; i < 3; i++)
+	{
+		readCoord();
+		readAngle();
+	}
+}
+
+void QWClientPrivate::parseSvcSpawnStatic()
+{
+	//printf("svc_spawnstatic\n");
+	//rawReadShort();
+	readByte();
+	readByte();
+	readByte();
+	readByte();
+	for(int i = 0; i < 3; i++)
+	{
+		readCoord();
+		readAngle();
+	}
+}
+
+void QWClientPrivate::parseSvcTempEntity()
+{
+	//printf("svc_temp_entity\n");
+	quint8 c = readByte();
+	bool	 parsed = false;
+
+	switch(c)
+	{
+		case TE_LIGHTNING1:
+		case TE_LIGHTNING2:
+		case TE_LIGHTNING3:
+			readShort();
+
+			readCoord();
+			readCoord();
+			readCoord();
+
+			readCoord();
+			readCoord();
+			readCoord();
+			parsed = true;
+			break;
+
+		case TE_GUNSHOT:
+			readByte();
+			readCoord();
+			readCoord();
+			readCoord();
+			parsed = true;
+			break;
+
+		case TE_BLOOD:
+			readByte();
+			readCoord();
+			readCoord();
+			readCoord();
+			parsed = true;
+			break;
+
+		case TE_LIGHTNINGBLOOD:
+			readCoord();
+			readCoord();
+			readCoord();
+			parsed = true;
+			break;
+	}
+
+	if(!parsed)
+	{
+		readCoord();
+		readCoord();
+		readCoord();
+	}
+}
+
+void QWClientPrivate::parseSvcUpdateStat()
+{
+	readByte();
+	readByte();
+}
+
+void QWClientPrivate::parseSvcUpdateStatLong()
+{
+	//printf("svc_updatestatlong\n");
+	readByte();
+	readLong();
+}
+
+void QWClientPrivate::parseSvcSpawnStaticSound()
+{
+	//printf("svc_spawnstaticsound\n");
+	readCoord();
+	readCoord();
+	readCoord();
+	readByte();
+	readByte();
+	readByte();
+}
+
+void QWClientPrivate::parseSvcCDTrack()
+{
+	readByte();
+}
+
+void QWClientPrivate::parseSvcIntermission()
+{
+	//printf("svc_intermission\n");
+	readCoord();
+	readCoord();
+	readCoord();
+	readAngle();
+	readAngle();
+	readAngle();
+}
+
+void QWClientPrivate::parseSvcFinale()
+{
+	//printf("svc_finale\n");
+	readString();
+}
+
+void QWClientPrivate::parseSvcSellScreen()
+{
+
+}
+
+void QWClientPrivate::parseSvcSmallKick()
+{
+
+}
+
+void QWClientPrivate::parseSvcBigKick()
+{
+
+}
+
+void QWClientPrivate::parseSvcMuzzleFlash()
+{
+	//printf("svc_muzzleflash\n");
+	readShort();
+}
+
+void QWClientPrivate::parseSvcUpdateUserinfo()
+{
+	quint8 playerNum = readByte();
+	quint32 userID = readLong();
+	QString info = readString();
+	myClient->onUpdateUserInfo(playerNum, userID, info.toAscii().data());
+}
+
+void QWClientPrivate::parseSvcSetinfo()
+{
+	//printf("svc_setinfo\n");
+	QString key, value;
+	int playerNum = readByte();
+	key = readString();
+	value = readString();
+
+	//that must be done
+	if(key == "rate")
+	{
+		myRate = value.toUInt();
+		sendCmd("setinfo \"rate\" \"" + value + "\"");
+	}
+
+	myClient->onSetInfo(playerNum, key.toAscii().data(), value.toAscii().data());
+}
+
+void QWClientPrivate::parseSvcServerinfo()
+{
+	//printf("svc_serverinfo\n");
+	QString key, value;
+
+	key = readString();
+	value = readString();
+	myClient->onServerInfo(key.toAscii().data(), value.toAscii().data());
+}
+
+void QWClientPrivate::startDownload(const QString &filename)
+{
+	if(myDownload->isOpen())
+		myDownload->close();
+
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, QString("download " + filename));
+
+	if(filename.contains('/'))
+	{
+		QString path = filename.mid(0, (filename.size() - filename.section('/', -1).size() - 1));
+		QDir quakeDir(myGameDir);
+		quakeDir.mkpath(myQuakeDir + "/" + myGameDir + "/" + path);
+	}
+
+	//generate disk path
+	int tmpNo = 0;
+	QString tmpFileName = myQuakeDir + "/" + myGameDir + "/" + filename + QString::number(tmpNo) + ".tmp";
+	while(myDownload->exists(tmpFileName))
+	{
+		tmpNo++;
+		tmpFileName = myQuakeDir + "/" + myGameDir + "/" + filename + QString::number(tmpNo) + ".tmp";
+	}
+
+	myDownload->setFileName(tmpFileName);
+	myDownload->open(QIODevice::WriteOnly);
+
+	myClient->onDownloadStarted(filename.toAscii().data());
+}
+
+#if 0
+void QWClientPrivate::parseChunkedDownload()
+{
+	QString	svname;
+	int			totalsize;
+	int			chunknum;
+	char		data[DLBLOCKSIZE];
+
+	chunknum = readLong();
+	if(chunknum < 0)
+	{
+		totalsize = readLong();
+		svname    = readString();
+
+		if(myDownload->isOpen())
+		{
+			// Ensure FILE is closed
+			if(totalsize != -3) // -3 = dl stopped, so this known issue, do not warn
+				qDebug("cls.download shouldn't have been set\n");
+			myDownload->close();
+		}
+
+		if(totalsize < 0)
+		{
+			switch(totalsize)
+			{
+				case -3: qDebug("Server cancel downloading file %s\n", svname.toAscii().data());			break;
+				case -2: qDebug("Server permissions deny downloading file %s\n", svname.toAscii().data());	break;
+				default: qDebug("Couldn't find file %s on the server\n", svname.toAscii().data());			break;
+			}
+			myDownload->close();
+			return;
+		}
+
+		if(cls.downloadmethod == DL_QWCHUNKS)
+		{
+			qDebug("Received second download - \"%s\"\n", svname.toAscii().data());
+			disconnect();
+		}
+
+		if(!myDownload->isOpen())
+		{
+			qDebug("Failed to open %s", myDownload->fileName());
+			return;
+		}
+
+		cls.downloadmethod  = DL_QWCHUNKS;
+		cls.downloadpercent = 0;
+
+		chunked_download_number++;
+
+		downloadsize        = totalsize;
+
+		firstblock    = 0;
+		receivedbytes = 0;
+		blockcycle    = -1;	//so it requests 0 first. :)
+		memset(recievedblock, 0, sizeof(recievedblock));
+		return;
+	}
+
+	MSG_ReadData(data, DLBLOCKSIZE);
+
+	if (myDownload->isOpen())
+		return;
+
+	if (cls.downloadmethod != DL_QWCHUNKS)
+		Host_Error("cls.downloadmethod != DL_QWCHUNKS\n");
+
+	if(chunknum < firstblock)
+		return;
+
+	if(chunknum - firstblock >= MAXBLOCKS)
+		return;
+
+	if(recievedblock[chunknum&(MAXBLOCKS-1)])
+		return;
+
+	receivedbytes += DLBLOCKSIZE;
+	recievedblock[chunknum&(MAXBLOCKS-1)] = true;
+
+	while(recievedblock[firstblock&(MAXBLOCKS-1)])
+	{
+		recievedblock[firstblock&(MAXBLOCKS-1)] = false;
+		firstblock++;
+	}
+
+	fseek(cls.download, chunknum * DLBLOCKSIZE, SEEK_SET);
+	if (downloadsize - chunknum * DLBLOCKSIZE < DLBLOCKSIZE)	//final block is actually meant to be smaller than we recieve.
+		fwrite(data, 1, downloadsize - chunknum * DLBLOCKSIZE, cls.download);
+	else
+		fwrite(data, 1, DLBLOCKSIZE, cls.download);
+
+	cls.downloadpercent = receivedbytes/(float)downloadsize*100;
+
+	tm = Sys_DoubleTime() - cls.downloadstarttime; // how long we dl-ing
+	cls.downloadrate = (tm ? receivedbytes / 1024 / tm : 0); // some average dl speed in KB/s
+}
+#endif
+
+void QWClientPrivate::parseSvcDownload()
+{
+//	if(myFTEProtocolExtensions & FTE_PEXT_CHUNKEDDOWNLOADS)
+//	{
+//		parseChunkedDownload();
+//		return;
+//	}
+
+	qint16 size;
+	quint8 percent;
+
+	size = readShort();
+	percent = readByte();
+
+	if(size == -1)
+	{
+		//file not found
+		disconnect();
+		return;
+	}
+
+	if(!myDownload->isOpen())
+	{
+		myInStream.device()->seek(myInStream.device()->pos() + size);
+		return;
+	}
+
+	char*	temp = new char[size];
+	myInStream.readRawData(temp, size);
+	myDownload->write(temp, size);
+  myDownload->waitForBytesWritten(1000);
+  delete[] temp;
+
+	myClient->onDownloadProgress(percent);
+	if(percent != 100)
+	{
+		writeByte(&myReliableOutStream, clc_stringcmd);
+		writeString(&myReliableOutStream, "nextdl");
+	}
+	else
+	{
+		myDownload->close();
+		QString normalFileName = myDownload->fileName().left(myDownload->fileName().length()-5); //strip #.tmp
+		if(!myDownload->rename(normalFileName))
+		{
+			QFile::remove(normalFileName);
+			myDownload->rename(normalFileName); //now it should succeed
+		}
+
+		myClient->onDownloadFinished();
+
+		preSpawn(mapChecksum(myMapName));
+	}
+}
+
+void QWClientPrivate::readUserDeltaCmd(userCmd_t *from, userCmd_t *move)
+{
+	quint8 bits;
+
+	memcpy(move, from, sizeof(*move));
+
+	bits = readByte();
+
+	// read current angles
+	if(bits & CM_ANGLE1)
+		move->angles[0] = readAngle16();
+	if(bits & CM_ANGLE2)
+		move->angles[1] = readAngle16();
+	if(bits & CM_ANGLE3)
+		move->angles[2] = readAngle16();
+
+	// read movement
+	if(bits & CM_FORWARD)
+		move->forwardmove = readShort();
+	if(bits & CM_SIDE)
+		move->sidemove = readShort();
+	if(bits & CM_UP)
+		move->upmove = readShort();
+
+	// read buttons
+	if(bits & CM_BUTTONS)
+		move->buttons = readByte();
+
+	if(bits & CM_IMPULSE)
+		move->impulse = readByte();
+
+	// read time to run command
+	move->msec = readByte();
+
+}
+
+void QWClientPrivate::parseSvcPlayerinfo()
+{
+	quint16		flags;
+	userCmd_t from, move;
+
+	memset(&from, 0, sizeof(userCmd_t));
+	memset(&move, 0, sizeof(userCmd_t));
+	int playerNum = readByte();
+	flags = readShort();
+	float x = readCoord();
+	float y = readCoord();
+	float z = readCoord();
+
+	myClient->onPlayerInfo(playerNum, x, y, z);
+
+	readByte();
+
+	if(flags & PF_MSEC)
+		readByte();
+	if(flags & PF_COMMAND)
+		readUserDeltaCmd(&from, &move);
+
+	for(int i = 0; i < 3; i++)
+	{
+		if(flags & (PF_VELOCITY1<<i))
+			readShort();
+	}
+	if(flags & PF_MODEL)
+		readByte();
+	if(flags & PF_SKINNUM)
+		readByte();
+	if(flags & PF_EFFECTS)
+		readByte();
+	if(flags & PF_WEAPONFRAME)
+		readByte();
+	if(flags & PF_TRANS_Z && myFTEProtocolExtensions & FTE_PEXT_TRANS)
+		readByte();
+}
+
+void QWClientPrivate::parseSvcNails()
+{
+	//printf("svc_nails\n");
+	quint8 c = readByte();
+  for(int i = 0; i < c; i++)
+	{
+		for(int j = 0; j < 6; j++)
+			readByte();
+	}
+}
+
+void QWClientPrivate::parseSvcChokeCount()
+{
+	readByte();
+}
+
+void QWClientPrivate::parseSvcModellist()
+{
+	quint8 i = readByte();
+	bool	 firstLoop = true;
+	for(;;)
+	{
+		QString s = readString();
+		if(s.isEmpty())
+			break;
+		if(!i && firstLoop)
+		{
+			myMapName = s;
+			firstLoop = false;
+		}
+		myClient->onModelListFile(s.toAscii().data());
+	}
+	i = readByte();
+	if(i)
+	{
+		writeByte(&myReliableOutStream, clc_stringcmd);
+		writeString(&myReliableOutStream, QString("modellist " + QString::number(myServerCount) + " " + QString::number(i)));
+		return;
+	}
+
+  if(!fileExists(myMapName) && !QWTables::getOriginalMapChecksum(myMapName))
+	{
+		startDownload(myMapName);
+		return;
+	}
+	preSpawn(mapChecksum(myMapName));
+}
+
+void QWClientPrivate::parseSvcSoundlist()
+{
+	quint8 i;
+
+	i = readByte();
+	for(;;)
+	{
+		QString s = readString();
+		if(s.isEmpty())
+			break;
+		myClient->onSoundListFile(s.toAscii().data());
+	}
+
+	i = readByte();
+	if(i)
+	{
+		writeByte(&myReliableOutStream, clc_stringcmd);
+		writeString(&myReliableOutStream, QString("soundlist " + QString::number(myServerCount) + " " + QString::number(i)));
+		return;
+	}
+
+	//continue login
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, QString("modellist " + QString::number(myServerCount) + " 0"));
+}
+
+void QWClientPrivate::parseSvcPacketEntities()
+{
+	int	word;
+	entityState_t	olde, newe;
+
+	memset(&olde, 0, sizeof(olde));
+
+	while (1)
+	{
+		word = (unsigned short)readShort();
+
+		if (!word)
+			break;	// done
+
+		parseDelta(&olde, &newe, word);
+	}
+}
+
+void QWClientPrivate::parseSvcDeltaPacketEntities()
+{
+	parseSvcPacketEntities();
+}
+
+void QWClientPrivate::parseSvcMaxSpeed()
+{
+	myClient->onMaxSpeedChange(readFloat());
+}
+
+void QWClientPrivate::parseSvcEntGravity()
+{
+	myClient->onEntGravityChange(readFloat());
+}
+
+void QWClientPrivate::parseSvcSetPause()
+{
+	myClient->onSetPause(readByte());
+}
+
+void QWClientPrivate::parseSvcNails2()
+{
+	quint8 c = readByte();
+  for(int i = 0; i < c; i++)
+	{
+		readByte();
+		for(int j = 0; j < 6; j++)
+			readByte();
+	}
+}
+
+void QWClientPrivate::parseSvcFTEModellistShort()
+{
+	quint16 i = readShort();
+	bool	  firstLoop = true;
+	for(;;)
+	{
+		QString s;
+		s = readString();
+		if(s.isEmpty())
+			break;
+		if(!i && firstLoop)
+		{
+			myMapName = s;
+			firstLoop = false;
+		}
+		myClient->onModelListFile(s.toAscii().data());
+	}
+	i = readByte();
+	if(i)
+	{
+		writeByte(&myReliableOutStream, clc_stringcmd);
+		writeString(&myReliableOutStream, QString("modellist " + QString::number(myServerCount) + " " + QString::number(i)));
+		return;
+	}
+
+  if(!fileExists(myMapName) && !QWTables::getOriginalMapChecksum(myMapName))
+	{
+		startDownload(myMapName);
+		return;
+	}
+	preSpawn(mapChecksum(myMapName));
+}
+
+void QWClientPrivate::parseSvcFTESpawnBaseline2()
+{
+	entityState_t nullst, es;
+
+	if(!(myFTEProtocolExtensions & FTE_PEXT_SPAWNSTATIC2))
+	{
+		myClient->onError("illegible server message\nsvc_fte_spawnbaseline2 without FTE_PEXT_SPAWNSTATIC2\n");
+		disconnect();
+		return;
+	}
+
+	memset(&nullst, 0, sizeof (entityState_t));
+	memset(&es, 0, sizeof (entityState_t));
+
+	parseDelta(&nullst, &es, readShort());
+}
+
+void QWClientPrivate::parseSvcQizmoVoice()
+{
+	// Read the two-byte header.
+	readByte();
+	readByte();
+
+	// 32 bytes of voice data follow
+	for(int i = 0; i < 32; i++)
+		readByte();
+}
+
+void QWClientPrivate::parseSvcFTEVoiceChat()
+{
+	readByte();
+	readByte();
+	readByte();
+	quint16 i = readShort();
+	myInStream.device()->seek(myInStream.device()->pos() + i);
+}
+
+void QWClientPrivate::parseSvcFTESpawnStatic2()
+{
+	if(myFTEProtocolExtensions & FTE_PEXT_SPAWNSTATIC2)
+	{
+		entityState_t from, to;
+		memset(&from, 0, sizeof(entityState_t));
+		memset(&to, 0, sizeof(entityState_t));
+    parseDelta(&from, &to, readShort());
+	}
+}
+
+void QWClientPrivate::parseSvcKilledMonster()
+{
+
+}
+
+void QWClientPrivate::parseSvcFoundSecret()
+{
+
+}
+
+void QWClientPrivate::parseSvcUpdateName()
+{
+  readByte();
+  readString();
+}
+
+void QWClientPrivate::parseSvcUpdateColors()
+{
+  readByte();
+  readByte();
+}
+
+void QWClientPrivate::parseSvcTime()
+{
+  readFloat();
+}
+
+void QWClientPrivate::parseSvcSignonNum()
+{
+  readByte();
+}
+
+void QWClientPrivate::parseSvcParticle()
+{
+  for(int i = 0; i < 3; ++i)
+    readCoord();
+  for(int i = 0; i < 3; ++i)
+    readByte();
+  readByte();
+  readByte();
+}
+
+void QWClientPrivate::parseSvcClientData()
+{
+  qDebug() << "WHY?!?!?!?!?!?! WHY ME BEING CALLED!";
+}
+
+void QWClientPrivate::parseSvcVersion()
+{
+
+}
+
+void QWClientPrivate::preSpawn(int mapChecksum)
+{
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, "setinfo pmodel 33168");
+
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, "setinfo emodel 6967");
+
+	writeByte(&myReliableOutStream, clc_stringcmd);
+	writeString(&myReliableOutStream, QString("prespawn " + QString::number(myServerCount) + " 0 " + QString::number(mapChecksum)));
+}
+
+void QWClientPrivate::parseServerMessage()
+{
+	quint32 incomingSeq, incomingAck;
+	bool		incomingSeqReliable, incomingAckReliable;
+
+	myBadReadFlag = false;
+
+	myInStream.device()->seek(0);
+	myInStream >> incomingSeq;
+	myInStream >> incomingAck;
+	incomingSeqReliable = incomingSeq >> 31;
+	incomingAckReliable = incomingAck >> 31;
+	incomingSeq &= ~0x80000000;
+	incomingAck &= ~0x80000000;
+
+	if(incomingSeq <= myIncomingSeq)
+		return;
+
+	myPacketLoss = incomingSeq - (myIncomingSeq + 1);
+
+	if(incomingAckReliable == myOutgoingSeqReliableFlag)
+		myReliableData.clear();
+
+	if(incomingSeqReliable)
+		myIncomingSeqReliableFlag ^= 1;
+
+	myIncomingSeq = incomingSeq;
+	myIncomingAck = incomingAck;
+	myIncomingAckReliableFlag = incomingAckReliable;
+
+	while(!myInStream.atEnd())
+	{
+		if(myBadReadFlag)
+		{
+			myClient->onError("Bad read from server.");
+			disconnect();
+			return;
+		}
+
+		quint8 c;
+		quint8 last = 0;
+
+		myInStream >> c;
+		if(c == 0xff)
+			break;
+
+		switch(c)
+		{
+			case svc_bad:
+				myClient->onError("Bad read from server.");
+        disconnect();
+        return;
+
+			case svc_nop:
+				parseSvcNoop();
+				break;
+
+			case svc_disconnect:
+				parseSvcDisconnect();
+				break;
+
+			case svc_print:
+				parseSvcPrint();
+				break;
+
+			case svc_centerprint:
+				parseSvcCenterPrint();
+				break;
+
+			case svc_stufftext:
+				parseSvcStuffText();
+				break;
+
+			case svc_damage:
+				parseSvcDamage();
+				break;
+
+			case svc_serverdata:
+				parseSvcServerData();
+				break;
+
+			case svc_setangle:
+				parseSvcSetAngle();
+				break;
+
+			case svc_lightstyle:
+				parseSvcLightStyle();
+				break;
+
+			case svc_sound:
+				parseSvcSound();
+				break;
+
+			case svc_stopsound:
+				parseSvcStopSound();
+				break;
+
+			case svc_updatefrags:
+				parseSvcUpdateFrags();
+				break;
+
+			case svc_updateping:
+				parseSvcUpdatePing();
+				break;
+
+			case svc_updatepl:
+				parseSvcUpdatePL();
+				break;
+
+			case svc_updateentertime:
+				parseSvcUpdateEnterTime();
+				break;
+
+			case svc_spawnbaseline:
+				parseSvcSpawnBaseLine();
+				break;
+
+			case svc_spawnstatic:
+				parseSvcSpawnStatic();
+				break;
+
+			case svc_temp_entity:
+				parseSvcTempEntity();
+				break;
+
+			case svc_killedmonster:
+				parseSvcKilledMonster();
+				break;
+
+			case svc_foundsecret:
+				parseSvcFoundSecret();
+				break;
+
+			case svc_updatestat:
+				parseSvcUpdateStat();
+				break;
+
+			case svc_updatestatlong:
+				parseSvcUpdateStatLong();
+				break;
+
+			case svc_spawnstaticsound:
+				parseSvcSpawnStaticSound();
+				break;
+
+			case svc_cdtrack:
+				parseSvcCDTrack();
+				break;
+
+			case svc_intermission:
+				parseSvcIntermission();
+				break;
+
+			case svc_finale:
+				parseSvcFinale();
+				break;
+
+			case svc_sellscreen:
+				parseSvcSellScreen();
+				break;
+
+			case svc_smallkick:
+				parseSvcSmallKick();
+				break;
+
+			case svc_bigkick:
+				parseSvcBigKick();
+				break;
+
+			case svc_muzzleflash:
+				parseSvcMuzzleFlash();
+				break;
+
+			case svc_updateuserinfo:
+				parseSvcUpdateUserinfo();
+				break;
+
+			case svc_setinfo:
+				parseSvcSetinfo();
+				break;
+
+			case svc_serverinfo:
+				parseSvcServerinfo();
+				break;
+
+			case svc_download:
+				parseSvcDownload();
+				break;
+
+			case svc_playerinfo:
+				parseSvcPlayerinfo();
+				break;
+
+			case svc_nails:
+				parseSvcNails();
+				break;
+
+			case svc_chokecount:
+				parseSvcChokeCount();
+				break;
+
+			case svc_modellist:
+				parseSvcModellist();
+				break;
+
+			case svc_soundlist:
+				parseSvcSoundlist();
+				break;
+
+			case svc_packetentities:
+				parseSvcPacketEntities();
+				break;
+
+			case svc_deltapacketentities:
+				parseSvcDeltaPacketEntities();
+				break;
+
+			case svc_maxspeed:
+				parseSvcMaxSpeed();
+				break;
+
+			case svc_entgravity:
+				parseSvcEntGravity();
+				break;
+
+    case svc_setpause:
+				parseSvcSetPause();
+				break;
+
+			case svc_nails2:
+				parseSvcNails2();
+				break;
+
+			case svc_fte_modellistshort:
+				parseSvcFTEModellistShort();
+				break;
+
+			case svc_fte_spawnbaseline2:
+				parseSvcFTESpawnBaseline2();
+				break;
+
+			case svc_qizmovoice:
+				parseSvcQizmoVoice();
+				break;
+
+			case svc_fte_voicechat:
+				parseSvcFTEVoiceChat();
+				break;
+
+			case svc_fte_spawnstatic2:
+				parseSvcFTESpawnStatic2();
+				break;
+			case nq_svc_time:
+        parseSvcTime();
+				break;
+			case nq_svc_clientdata:
+        parseSvcClientData();
+        break;
+			case nq_svc_version:
+        parseSvcVersion();
+        break;
+			case nq_svc_particle:
+        parseSvcParticle();
+        break;
+			case nq_svc_signonnum:
+        parseSvcSignonNum();
+        break;
+			case nq_svc_updatecolors:
+        parseSvcUpdateColors();
+        break;
+			case nq_svc_updatename:
+        parseSvcUpdateName();
+        break;
+			default:
+				myClient->onError(QString("Unknown message from server. Last Cmd: [" + QString::number(last) + "] Current Cmd: [" + QString::number(c) + "]").toAscii().data());
+        disconnect();
+        return;
+		}
+		last = c;
+	}
+}
+
+void QWClientPrivate::connect(const char *host, quint16 port)
+{
+  if(myState != QWClient::DisconnectedState)
+		return;
+
+  /* Disabled this, blocking too much, now user is supposed to send the string already resolved. */
+  //	QHostInfo hi = QHostInfo::fromName(host);
+  //	if(hi.error() != QHostInfo::NoError)
+  //	{
+  //		myClient->onError(hi.errorString().toAscii().data());
+  //		return;
+  //	}
+
+  myHost.setAddress(host);
+	myPort = port;
+
+	myIncomingSeq = 0;
+	myIncomingAck = 0;
+	myOutgoingSeq = 0;
+	myLastRealiableSeq = 0;
+	myIncomingSeqReliableFlag = false;
+	myIncomingAckReliableFlag = false;
+	myOutgoingSeqReliableFlag = false;
+	myPacketLoss = 0;
+
+	mySocket->connectToHost(myHost, myPort);
+	mySocket->waitForConnected();
+	sendConnectionless("getchallenge\n");
+	myClient->onChallenge();
+	myQPort = qrand() & 0xffff;
+	myTime->start();
+
+  myState = QWClient::ConnectingState;
+
+	*myLastServerReplyTime = QTime::currentTime();
+}
+
+void QWClientPrivate::setBindHost(const QString &host)
+{
+  QHostAddress address(host);
+  mySocket->bind(address, 0);
+}
+
+void QWClientPrivate::disconnect()
+{
+  if(myState == QWClient::ConnectedState)
+	{
+    writeByte(&myUnreliableOutStream, clc_stringcmd);
+    writeString(&myUnreliableOutStream, "drop");
+    sendToServer(true);
+    writeByte(&myUnreliableOutStream, clc_stringcmd);
+    writeString(&myUnreliableOutStream, "drop");
+    sendToServer(true);
+    writeByte(&myUnreliableOutStream, clc_stringcmd);
+    writeString(&myUnreliableOutStream, "drop");
+    sendToServer(true);
+	}
+  mySocket->close();
+  myState = QWClient::DisconnectedState;
+}
+
+void QWClientPrivate::sendConnectionless(const QByteArray &data)
+{
+	QByteArray d;
+	d.append("\xff\xff\xff\xff");
+	d.append(data);
+	mySocket->write(d);
+  mySocket->waitForBytesWritten();
+}
+
+void QWClientPrivate::sendMovement()
+{
+  myUnreliableOutStream << (quint8)clc_move << (quint8)0x10 << (quint8)myPacketLoss << (quint8)0x00 << (quint8)0x00 << (quint8)0x00 << (quint8)0x22 << (quint8)0x00 << (quint8)0x21;
+}
+
+//=====================================================================
+
+// "GPL map" support.  If we encounter a map with a known "GPL" CRC,
+// we fake the CRC so that, on the client side, the CRC of the original
+// map is transferred to the server, and on the server side, comparison
+// of clients' CRC is done against the orignal one
+typedef struct {
+	const char *mapname;
+	int original;
+	int gpl;
+} csentry_t;
+
+static csentry_t table[] = {
+	// CRCs for AquaShark's "simpletextures" maps
+  { "dm1", 0xc5c7dab3, 0x7d37618e },
+	{ "dm2", 0x65f63634, 0x7b337440 },
+	{ "dm3", 0x15e20df8, 0x912781ae },
+	{ "dm4", 0x9c6fe4bf, 0xc374df89 },
+	{ "dm5", 0xb02d48fd, 0x77ca7ce5 },
+	{ "dm6", 0x5208da2b, 0x200c8b5d },
+	{ "end", 0xbbd4b4a5, 0xf89b12ae }, // this is the version with the extra room
+	{ NULL, 0, 0 },
+};
+
+int Com_TranslateMapChecksum (const char *mapname, int checksum)
+{
+	csentry_t *p;
+
+//	Com_Printf ("Map checksum (%s): 0x%x\n", mapname, checksum);
+
+	for (p = table; p->mapname; p++)
+		if (!strcmp(p->mapname, mapname)) {
+			if (checksum == p->gpl)
+				return p->original;
+			else
+				return checksum;
+		}
+
+	return checksum;
+}
+
+quint32 QWClientPrivate::mapChecksum(const QString &mapName)
+{
+  // Check if this is an original map, if it is we have the checksum table ready
+  quint32     checksum = 0;
+  checksum = QWTables::getOriginalMapChecksum(mapName);
+  if(checksum)
+    return checksum;
+
+	char*		mapdata;
+	quint64 maplen;
+
+	if(!readFile(mapName, &mapdata, &maplen))
+		return 0;
+  if(!maplen || !mapdata)
+    return 0;
+
+	dheader_t* header;
+	uchar*		 mod_base;
+
+	header = (dheader_t*)mapdata;
+	mod_base = (uchar*)mapdata;
+
+	for(int i = 0; i < HEADER_LUMPS; ++i)
+	{
+		if(i == LUMP_ENTITIES || i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)
+			continue;
+		checksum ^= blockCheckSum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
+	}
+	delete[] mapdata;
+
+  QString	cleanMapName;
+  cleanMapName = mapName.section('/', -1);
+  cleanMapName.chop(4); //strip .bsp
+
+	return Com_TranslateMapChecksum(cleanMapName.toAscii().data(), checksum);
+}
+
+unsigned QWClientPrivate::blockCheckSum(void* buffer, int len)
+{
+	QByteArray digest = QCryptographicHash::hash(QByteArray((const char*)buffer, len), QCryptographicHash::Md4);
+	int *d = (int*)digest.data();
+	return (d[0] ^ d[1] ^ d[2] ^ d[3]);
+}
+
+void QWClientPrivate::writeByte(QDataStream *stream, const quint8 b)
+{
+	*stream << b;
+}
+
+void QWClientPrivate::writeLong(QDataStream *stream, const quint32 l)
+{
+	*stream << l;
+}
+
+void QWClientPrivate::writeShort(QDataStream *stream, const quint16 s)
+{
+	*stream << s;
+}
+
+void QWClientPrivate::writeString(QDataStream *stream, const QString &str)
+{
+	stream->writeRawData(str.toAscii().data(), str.size()+1);
+}
+
+//========================================================================
+// NAME FUN
+
+char QWClientPrivate::ourReadableCharsTable[256] = {	'.', '_' , '_' , '_' , '_' , '.' , '_' , '_' , '_' , '_' , '\n' , '_' , '\n' , '>' , '.' , '.',
+																			 '[', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_', '_', '_'
+																			 };
+bool QWClientPrivate::ourReadableCharsTableInitialized = false;
+
+void QWClientPrivate::fillReadableCharsTable()
+{
+	int i;
+
+	for(i = 32; i < 127; i++)
+		ourReadableCharsTable[i] = ourReadableCharsTable[128 + i] = i;
+	ourReadableCharsTable[127] = ourReadableCharsTable[128 + 127] = '_';
+
+	for(i = 0; i < 32; i++)
+		ourReadableCharsTable[128 + i] = ourReadableCharsTable[i];
+	ourReadableCharsTable[128] = '_';
+	ourReadableCharsTable[10 + 128] = '_';
+	ourReadableCharsTable[12 + 128] = '_';
+
+	ourReadableCharsTableInitialized = true;
+}
+
+void QWClientPrivate::stripColor(char* string)
+{
+	if(!ourReadableCharsTableInitialized)
+		fillReadableCharsTable();
+
+	while(*string)
+	{
+		*string = ourReadableCharsTable[(unsigned char)*string] & 127;
+		string++;
+	}
+}

+ 245 - 0
QWClientPrivate.h

@@ -0,0 +1,245 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#ifndef QWCLIENTPRIVATE_H
+#define QWCLIENTPRIVATE_H
+
+#include <qglobal.h>
+#include <QBuffer>
+#include <QDataStream>
+#include <QHostAddress>
+#include <QList>
+#include "QWClient.h"
+#include "quakedef.h"
+
+class QWClient;
+class QWPack;
+class QWClientPrivate
+{
+public:
+	QWClientPrivate(QWClient *client);
+	~QWClientPrivate();
+
+	void							connect(const char *host, quint16 port);
+	void							run();
+  void							disconnect();
+	void							observe();
+	void							join();
+  void              setBindHost(const QString& host);
+	void							setName(const char *name);
+	void							setTeam(const char *team);
+	void							setColor(quint8 bottom, quint8 top);
+	void							setQuakeFolder(const QString& path);
+	void							setSpectator(bool spectate = true);
+  void              setPassword(const QString& password);
+	void							setPing(quint16 ping);
+	void							setRate(quint16 rate);
+	void							sendCmd(const QString& cmd);
+  const QString&    gameDir() const;
+  const QString&    quakeDir() const;
+	void							reconnect();
+	const QString			host() const { return myHost.toString(); }
+	quint16						port() const { return myPort; }
+	QWClient::ClientState state() const { return myState; }
+	static void				stripColor(char* string);
+
+private:
+	class QWClient*		myClient;
+	class QUdpSocket* mySocket;
+	class QTime*			myTime;
+	class QTime*			myLastServerReplyTime;
+	class QFile*			myDownload; //current active download
+
+	QHostAddress			myHost;
+	quint16						myPort;
+
+	/* Incoming data buffers */
+	QByteArray				myInData;
+	QBuffer						myInBuffer;
+	QDataStream				myInStream;
+
+	/* Outgoing client data buffers */
+	QDataStream				myUnreliableOutStream;
+	QBuffer						myUnreliableOutBuffer;
+	QByteArray				myUnreliableOutData;
+
+	QDataStream				myReliableOutStream;
+	QBuffer						myReliableOutBuffer;
+	QByteArray				myReliableOutData;
+	QByteArray				myReliableData;		//saves reliable messages not acked
+
+	/* Everything is assembled here b4 sending */
+	QDataStream				myOutStream;
+	QBuffer						myOutBuffer;
+	QByteArray				myOutData;
+
+	bool							myBadReadFlag;
+
+	static const char*ClientName;
+	static const char*ClientVersion;
+	QString						myClientName;
+	QString						myClientVersion;
+
+	QWClient::ClientState	myState;
+	quint16						myQPort;
+	quint32						myProtocolVersion;
+	quint32						myFTEProtocolExtensions;
+	quint32						myServerCount;
+  QString						myGameDir;
+	QString						myQuakeDir;
+	QString						myMapName;
+  QString           myPassword; // For connecting on servers that require a password
+
+	quint32						myIncomingSeq;
+	quint32						myIncomingAck;
+	quint32						myOutgoingSeq;
+	quint32						myLastRealiableSeq;
+	bool							myIncomingSeqReliableFlag;
+	bool							myIncomingAckReliableFlag;
+	bool							myOutgoingSeqReliableFlag;
+	quint8						myPacketLoss;
+	quint16						myPing;
+
+	/* Download */
+	QList<QWPack*>		myPacks;
+
+	/* Client Cvars */
+	quint16						myRate;
+	quint8						myTopColor;
+	quint8						myBottomColor;
+	QString						myName;
+	bool							mySpectatorFlag;
+	QString						myTeam;
+
+	/* NameFun Conversion */
+	static char				ourReadableCharsTable[256];
+	static bool				ourReadableCharsTableInitialized;
+	static void				fillReadableCharsTable();
+
+	void							reloadPackFiles();
+	void							loadPackFile(const QString& filename);
+
+	void							sendConnectionless(const QByteArray& data);
+	void							sendMovement(); //required on MVDSV
+  void							sendToServer(bool dontWait = false);
+	void							readPackets();
+
+	void							startDownload(const QString& filename);
+
+	bool							fileExists(const QString& filename);
+	bool							readFile(const QString& filename, char **data, quint64 *len);
+
+	void							preSpawn(int mapChecksum);
+
+	static unsigned		blockCheckSum(void *buffer, int len);
+	quint32						mapChecksum(const QString& mapName);
+
+	//========================================================================
+	// Parsing functions
+	/* Main parsing functions */
+	void							parseServerMessage();
+	void							parseConnectionless();
+
+	/* Helpers */
+	static float			littleFloat(float f);
+	static quint16		littleShort(quint16 s);
+	static quint32		littleLong(quint32 l);
+
+	/* Reading */
+	bool							checkForBadRead(quint8 typeSize);
+	float							readCoord();
+	float							readAngle();
+	float							readAngle16();
+	quint8						readByte();
+	float							readFloat();
+	qint16						readShort();
+	qint32						readLong();
+	const QString			readString();
+	void							readUserDeltaCmd(userCmd_t *from, userCmd_t *move);
+	void							parseDelta(entityState_t *from, entityState_t *to, int bits);
+
+	/* Writing */
+	static void				writeByte(QDataStream* stream, const quint8 b);
+	static void				writeShort(QDataStream* stream, const quint16 s);
+	static void				writeLong(QDataStream* stream, const quint32 l);
+	static void				writeString(QDataStream* stream, const QString& str);
+
+
+	/* Command parsers */
+  void							parseSvcModellist();//s
+  void							parseSvcFTEModellistShort();//s
+  void							parseSvcDisconnect();//
+  void							parseSvcNoop();//
+  void							parseSvcPrint();//
+  void							parseSvcCenterPrint();//
+  void							parseSvcStuffText();//
+  void							parseSvcDamage();//
+  void							parseSvcServerData();//s
+  void							parseSvcSetAngle();//
+  void							parseSvcLightStyle();//
+  void							parseSvcSound();//
+  void							parseSvcStopSound();//
+  void							parseSvcUpdateFrags();//
+  void							parseSvcUpdatePing();//
+  void							parseSvcUpdatePL();//
+  void							parseSvcUpdateEnterTime();//
+  void							parseSvcSpawnBaseLine();//
+  void							parseSvcSpawnStatic();//
+  void							parseSvcTempEntity();//
+  void							parseSvcKilledMonster();//
+  void							parseSvcFoundSecret();//
+  void							parseSvcUpdateStat();//
+  void							parseSvcUpdateStatLong();//
+  void							parseSvcSpawnStaticSound();//
+  void							parseSvcCDTrack();//
+  void							parseSvcIntermission();//
+  void							parseSvcFinale();//
+  void							parseSvcSellScreen();//
+  void							parseSvcSmallKick();//
+  void							parseSvcBigKick();//
+  void							parseSvcMuzzleFlash();//
+  void							parseSvcUpdateUserinfo();//
+  void							parseSvcSetinfo();//
+  void							parseSvcServerinfo();//
+  void							parseSvcDownload();//
+  void							parseChunkedDownload();//NA
+  void							parseSvcPlayerinfo();//
+  void							parseSvcNails();//fixed
+  void							parseSvcChokeCount();//
+  void							parseSvcSoundlist();//
+  void							parseSvcPacketEntities();//look at it
+  void							parseSvcDeltaPacketEntities();//look at it
+  void							parseSvcMaxSpeed();//
+  void							parseSvcEntGravity();//
+  void							parseSvcSetPause();//
+  void							parseSvcNails2();//fixed
+  void							parseSvcFTESpawnBaseline2();//
+  void							parseSvcQizmoVoice();//
+  void							parseSvcFTEVoiceChat();//hmmf
+  void							parseSvcFTESpawnStatic2();//FIXED readbyte supposed to be readShort.
+  void              parseSvcUpdateName();
+  void              parseSvcUpdateColors();
+  void              parseSvcSignonNum();
+  void              parseSvcParticle();
+  void              parseSvcVersion();
+  void              parseSvcClientData();
+  void              parseSvcTime();
+};
+
+#endif // QWCLIENTPRIVATE_H

+ 148 - 0
QWPack.cpp

@@ -0,0 +1,148 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#include "QWPack.h"
+#include <QStringList>
+
+#define MAX_FILES_IN_PACK 2048
+
+//on disk
+typedef struct
+{
+	char	name[56];
+	int		filePos, fileLen;
+} dpackFile_t;
+
+typedef struct
+{
+	char	id[4];
+	int		dirofs;
+	int		dirlen;
+} dpackHeader_t;
+
+QWPack::QWPack()
+{
+}
+
+bool QWPack::load(const QString& filename)
+{
+	QFile packFile(filename);
+	if(!packFile.open(QIODevice::ReadOnly))
+		return false;
+	myFilename = filename;
+
+	dpackHeader_t header;
+	packFile.read((char*)&header, sizeof(dpackHeader_t));
+	if(
+	header.id[0] != 'P' ||
+	header.id[1] != 'A' ||
+	header.id[2] != 'C' ||
+	header.id[3] != 'K'
+	)
+	{
+		packFile.close();
+		return false;
+	}
+
+	int fileCount;
+	fileCount = header.dirlen / sizeof(dpackFile_t);
+	if(fileCount > MAX_FILES_IN_PACK)
+	{
+		packFile.close();
+		return false;
+	}
+
+	packFile.seek(header.dirofs);
+	dpackFile_t *packData = new dpackFile_t[fileCount];
+	packFile.read((char*)packData, header.dirlen);
+
+	for(int i = 0; i < fileCount; ++i)
+	{
+		PackedFile file;
+
+		file.name = packData[i].name;
+		file.filePos = packData[i].filePos;
+		file.fileLen = packData[i].fileLen;
+
+		myFiles.push_back(file);
+	}
+	delete [] packData;
+	packFile.close();
+
+	return true;
+}
+
+bool QWPack::exists(const QString &filename) const
+{
+	QList<PackedFile>::const_iterator itr = myFiles.constBegin();
+
+	while(itr != myFiles.constEnd())
+	{
+		const PackedFile* p = &(*itr);
+		if(p->name == filename)
+			return true;
+		itr++;
+	}
+	return false;
+}
+
+QStringList QWPack::files() const
+{
+  QStringList list;
+
+  QList<PackedFile>::const_iterator itr = myFiles.constBegin();
+
+  while(itr != myFiles.constEnd())
+  {
+    const PackedFile* p = &(*itr);
+    list.push_back(p->name);
+    itr++;
+  }
+  return list;
+}
+
+bool QWPack::read(const QString &filename, char **fileData, quint64 *len) const
+{
+	QList<PackedFile>::const_iterator itr = myFiles.constBegin();
+
+	while(itr != myFiles.constEnd())
+	{
+		const PackedFile* p = &(*itr);
+		if(p->name == filename)
+		{
+			QFile packFile(myFilename);
+			if(!packFile.open(QIODevice::ReadOnly))
+				return false;
+
+			packFile.seek(p->filePos);
+			*fileData = new char[p->fileLen];
+			packFile.read(*fileData, p->fileLen);
+			*len = p->fileLen;
+			return true;
+		}
+		itr++;
+	}
+	return false;
+}
+
+
+const QString& QWPack::fileName() const
+{
+	return myFilename;
+}

+ 47 - 0
QWPack.h

@@ -0,0 +1,47 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#ifndef QWPACK_H
+#define QWPACK_H
+
+#include <QString>
+#include <QFile>
+
+class QWPack
+{
+public:
+	QWPack();
+	bool	load(const QString &filename);
+	bool	exists(const QString &filename) const;
+	bool	read(const QString &filename, char **read, quint64 *len) const;
+	const QString& fileName() const;
+  QStringList files() const;
+
+private:
+	typedef struct
+	{
+		QString	name;
+		int	filePos, fileLen;
+	} PackedFile;
+
+	QString						myFilename;
+	QList<PackedFile> myFiles;
+};
+
+#endif // QWPACK_H

+ 76 - 0
QWTables.cc

@@ -0,0 +1,76 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#include "QWTables.h"
+#include <QString>
+
+const QWTables::OriginalChecksumTable QWTables::ourOriginalChecksumTable[] = {
+  { "maps/start.bsp", 493454459 },
+  { "maps/e1m1.bsp", 2902972546 },
+  { "maps/e1m2.bsp", 1729102119 },
+  { "maps/e1m3.bsp", 893792842 },
+  { "maps/e1m4.bsp", 3990488693 },
+  { "maps/e1m5.bsp", 2821463178 },
+  { "maps/e1m6.bsp", 738207971 },
+  { "maps/e1m7.bsp", 2547448602 },
+  { "maps/e1m8.bsp", 79095617 },
+  { "maps/e2m1.bsp", 3707072562 },
+  { "maps/e2m2.bsp", 2945850701 },
+  { "maps/e2m3.bsp", 4237894993 },
+  { "maps/e2m4.bsp", 3273038793 },
+  { "maps/e2m5.bsp", 3204615999 },
+  { "maps/e2m6.bsp", 2443393921 },
+  { "maps/e2m7.bsp", 2051006488 },
+  { "maps/e3m1.bsp", 2427587873 },
+  { "maps/e3m2.bsp", 2624353592 },
+  { "maps/e3m3.bsp", 3285212440 },
+  { "maps/e3m4.bsp", 2977500344 },
+  { "maps/e3m5.bsp", 2440693297 },
+  { "maps/e3m6.bsp", 767655416 },
+  { "maps/e3m7.bsp", 272220593 },
+  { "maps/e4m1.bsp", 3153093456 },
+  { "maps/e4m2.bsp", 4294495000 },
+  { "maps/e4m3.bsp", 1505685644 },
+  { "maps/e4m4.bsp", 758847551 },
+  { "maps/e4m5.bsp", 1771890676 },
+  { "maps/e4m6.bsp", 102825880 },
+  { "maps/e4m7.bsp", 2649489836 },
+  { "maps/e4m8.bsp", 1018457175 },
+  { "maps/end.bsp", 3151279269 },
+  { "maps/dm1.bsp", 3318209203 },
+  { "maps/dm2.bsp", 1710634548 },
+  { "maps/dm3.bsp", 367136248 },
+  { "maps/dm4.bsp", 2624578751 },
+  { "maps/dm5.bsp", 2955757821 },
+  { "maps/dm6.bsp", 1376311851 },
+  { NULL, 0 }
+};
+
+quint32 QWTables::getOriginalMapChecksum(const QString &mapName)
+{
+  for(int i = 0; ; ++i)
+  {
+    if(!ourOriginalChecksumTable[i].mapname)
+      return 0;
+
+    if(mapName == QString(ourOriginalChecksumTable[i].mapname))
+      return ourOriginalChecksumTable[i].checksum;
+  }
+  return 0;
+}

+ 49 - 0
QWTables.h

@@ -0,0 +1,49 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#ifndef QWTABLES_H
+#define QWTABLES_H
+
+#include <QtGlobal>
+
+class QWTables
+{
+public:
+  /**
+    Returns the checksum for an original ID1 map.
+
+    @param  mapName The absolute map name path
+    @return The checksum if found otherwise 0
+  */
+  static quint32 getOriginalMapChecksum(const QString& mapName);
+
+  private:
+  ///////////////////////////////////
+  // Original ID1 Map checksums
+  ///////////////////////////////////
+  struct OriginalChecksumTable
+  {
+    const char *mapname;
+    quint32 checksum;
+  };
+  static const OriginalChecksumTable ourOriginalChecksumTable[];
+  QWTables();
+};
+
+#endif // QWTABLES_H

+ 310 - 0
quakedef.h

@@ -0,0 +1,310 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#ifndef QUAKEDEF_H
+#define QUAKEDEF_H
+
+// for chunked downloads
+#define MAXBLOCKS		1024	// Must be power of 2
+#define DLBLOCKSIZE 1024
+#define DL_QWCHUNKS 2
+
+/* Needed definitions taken from EZQuake Source */
+
+typedef float vec3_t[3];
+
+typedef struct
+{
+	int			number;			// edict index
+	int			flags;			// nolerp, etc
+	vec3_t	origin;
+	vec3_t	angles;
+	int			modelindex;
+	int			frame;
+	int			colormap;
+	int			skinnum;
+	int			effects;
+} entityState_t;
+
+#define MAX_PACKET_ENTITIES	64
+
+typedef struct
+{
+	int						numentities;
+	entityState_t	entities[MAX_PACKET_ENTITIES];
+} packetEntities_t;
+
+#define	PROTOCOL_VERSION	28
+#define PROTOCOL_VERSION_FTE	(('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24)) //fte extensions.
+#define PROTOCOL_VERSION_FTE2	(('F'<<0) + ('T'<<8) + ('E'<<16) + ('2' << 24))	//fte extensions.
+
+#ifdef PROTOCOL_VERSION_FTE
+	#define	FTE_PEXT_TRANS							0x00000008	// .alpha support
+	#define FTE_PEXT_HLBSP							0x00000200	//stops fte servers from complaining
+	#define FTE_PEXT_MODELDBL						0x00001000
+	#define FTE_PEXT_ENTITYDBL					0x00002000	//max of 1024 ents instead of 512
+	#define FTE_PEXT_ENTITYDBL2					0x00004000	//max of 1024 ents instead of 512
+	#define FTE_PEXT_FLOATCOORDS				0x00008000	//supports floating point origins.
+	#define FTE_PEXT_SPAWNSTATIC2				0x00400000	//Sends an entity delta instead of a baseline.
+	#define FTE_PEXT_256PACKETENTITIES	0x01000000	//Client can recieve 256 packet entities.
+	#define FTE_PEXT_CHUNKEDDOWNLOADS		0x20000000	//alternate file download method. Hopefully it'll give quadroupled download speed, especially on higher pings.
+#endif // PROTOCOL_VERSION_FTE
+
+#ifdef PROTOCOL_VERSION_FTE2
+	#define FTE_PEXT2_VOICECHAT			0x00000002
+#endif // PROTOCOL_VERSION_FTE2
+
+#ifdef PROTOCOL_VERSION_FTE
+	#define U_FTE_EVENMORE	(1<<7)		//extension info follows
+	//EVENMORE flags
+	#ifdef FTE_PEXT_SCALE
+		#define U_FTE_SCALE		(1<<0)		//scaler of alias models
+	#endif
+	#ifdef FTE_PEXT_TRANS
+		#define U_FTE_TRANS		(1<<1)		//transparency value
+	#endif
+	#ifdef FTE_PEXT_TRANS
+		#define	PF_TRANS_Z			(1<<17)
+	#endif
+	#ifdef FTE_PEXT_FATNESS
+		#define U_FTE_FATNESS	(1<<2)		//byte describing how fat an alias model should be.
+									//moves verticies along normals
+									// Useful for vacuum chambers...
+	#endif
+	#ifdef FTE_PEXT_MODELDBL
+		#define U_FTE_MODELDBL	(1<<3)		//extra bit for modelindexes
+	#endif
+	#define U_FTE_UNUSED1	(1<<4)
+	#ifdef FTE_PEXT_ENTITYDBL
+		#define U_FTE_ENTITYDBL	(1<<5)		//use an extra byte for origin parts, cos one of them is off
+	#endif
+	#ifdef FTE_PEXT_ENTITYDBL2
+		#define U_FTE_ENTITYDBL2 (1<<6)		//use an extra byte for origin parts, cos one of them is off
+	#endif
+	#define U_FTE_YETMORE	(1<<7)		//even more extension info stuff.
+	#define U_FTE_DRAWFLAGS	(1<<8)		//use an extra qbyte for origin parts, cos one of them is off
+	#define U_FTE_ABSLIGHT	(1<<9)		//Force a lightlevel
+	#define U_FTE_COLOURMOD	(1<<10)		//rgb
+	#define U_FTE_DPFLAGS (1<<11)
+	#define U_FTE_TAGINFO (1<<12)
+	#define U_FTE_LIGHT (1<<13)
+	#define	U_FTE_EFFECTS16	(1<<14)
+	#define U_FTE_FARMORE (1<<15)
+#endif
+
+#define QW_CHECK_HASH 0x5157
+
+//=========================================
+
+#define	PORT_CLIENT	27001
+#define	PORT_MASTER	27000
+#define	PORT_SERVER	27500
+
+// out of band message id bytes
+// M = master, S = server, C = client, A = any
+// the second character will always be \n if the message isn't a single
+// byte long (?? not true anymore?)
+
+#define	S2C_CHALLENGE				'c'
+#define	S2C_CONNECTION			'j'
+#define	A2A_PING						'k'	// respond with an A2A_ACK
+#define	A2A_ACK							'l'	// general acknowledgement without info
+#define	A2A_NACK						'm'	// [+ comment] general failure
+#define A2A_ECHO						'e' // for echoing
+#define	A2C_PRINT						'n'	// print a message on client
+#define	S2M_HEARTBEAT				'a'	// + serverinfo + userlist + fraglist
+#define	A2C_CLIENT_COMMAND	'B'	// + command line
+#define	S2M_SHUTDOWN				'C'
+
+//
+// server to client
+//
+#define	svc_bad									0
+#define	svc_nop									1
+#define	svc_disconnect					2
+#define	svc_updatestat					3	// [byte] [byte]
+#define	nq_svc_version					4	// [long] server version
+#define	svc_setview							5	// [short] entity number
+#define	svc_sound								6	// <see code>
+#define	nq_svc_time							7	// [float] server time
+#define	svc_print								8	// [byte] id [string] null terminated string
+#define	svc_stufftext						9	// [string] stuffed into client's console buffer
+#define	svc_setangle						10	// [angle3] set the view angle to this absolute value
+#define	svc_serverdata					11	// [long] protocol ...
+#define	svc_lightstyle					12	// [byte] [string]
+#define	nq_svc_updatename				13	// [byte] [string]
+#define	svc_updatefrags					14	// [byte] [short]
+#define	nq_svc_clientdata				15	// <shortbits + data>
+#define	svc_stopsound						16	// <see code>
+#define	nq_svc_updatecolors			17	// [byte] [byte] [byte]
+#define	nq_svc_particle					18	// [vec3] <variable>
+#define	svc_damage							19
+#define	svc_spawnstatic					20
+#define	svc_fte_spawnstatic2		21		// @!@!@!
+#define	svc_spawnbaseline				22
+#define	svc_temp_entity					23	// variable
+#define	svc_setpause						24	// [byte] on / off
+#define	nq_svc_signonnum				25	// [byte]  used for the signon sequence
+#define	svc_centerprint					26	// [string] to put in center of the screen
+#define	svc_killedmonster				27
+#define	svc_foundsecret					28
+#define	svc_spawnstaticsound		29    // [coord3] [byte] samp [byte] vol [byte] aten
+#define	svc_intermission				30		// [vec3_t] origin [vec3_t] angle
+#define	svc_finale							31		// [string] text
+#define	svc_cdtrack							32		// [byte] track
+#define svc_sellscreen					33
+#define	svc_smallkick						34		// set client punchangle to 2
+#define	svc_bigkick							35		// set client punchangle to 4
+#define	svc_updateping					36		// [byte] [short]
+#define	svc_updateentertime			37		// [byte] [float]
+#define	svc_updatestatlong			38		// [byte] [long]
+#define	svc_muzzleflash					39		// [short] entity
+#define	svc_updateuserinfo			40		// [byte] slot [long] uid
+#define	svc_download						41		// [short] size [size bytes]
+#define	svc_playerinfo					42		// variable
+#define	svc_nails								43		// [byte] num [48 bits] xyzpy 12 12 12 4 8
+#define	svc_chokecount					44		// [byte] packets choked
+#define	svc_modellist						45		// [strings]
+#define	svc_soundlist						46		// [strings]
+#define	svc_packetentities			47		// [...]
+#define	svc_deltapacketentities	48		// [...]
+#define svc_maxspeed						49		// maxspeed change, for prediction
+#define svc_entgravity					50		// gravity change, for prediction
+#define svc_setinfo							51		// setinfo on a client
+#define svc_serverinfo					52		// serverinfo
+#define svc_updatepl						53		// [byte] [byte]
+#define svc_nails2							54		// [byte] num [52 bits] nxyzpy 8 12 12 12 4 8
+#define	svc_fte_modellistshort	60		// [strings]
+#define svc_fte_spawnbaseline2	66
+#define svc_qizmovoice					83
+#define svc_fte_voicechat				84
+
+//
+// client to server
+//
+#define	clc_bad				0
+#define	clc_nop				1
+#define	clc_move			3		// [[usercmd_t]
+#define	clc_stringcmd	4		// [string] message
+#define	clc_delta			5		// [byte] sequence number, requests delta compression of message
+#define clc_tmove			6		// teleport request, spectator only
+#define clc_upload		7		// teleport request, spectator only
+
+typedef struct userCmd_s
+{
+	unsigned char	msec;
+	float			angles[3];
+	short			forwardmove, sidemove, upmove;
+	unsigned char	buttons;
+	unsigned char	impulse;
+} userCmd_t;
+
+#define	PF_MSEC					(1<<0)
+#define	PF_COMMAND			(1<<1)
+#define	PF_VELOCITY1		(1<<2)
+#define	PF_VELOCITY2		(1<<3)
+#define	PF_VELOCITY3		(1<<4)
+#define	PF_MODEL				(1<<5)
+#define	PF_SKINNUM			(1<<6)
+#define	PF_EFFECTS			(1<<7)
+#define	PF_WEAPONFRAME	(1<<8)		// only sent for view player
+#define	PF_DEAD					(1<<9)		// don't block movement any more
+#define	PF_GIB					(1<<10)		// offset the view height differently
+#define	PF_NOGRAV				(1<<11)		// don't apply gravity for prediction
+#define	CM_ANGLE1				(1<<0)
+#define	CM_ANGLE3				(1<<1)
+#define	CM_FORWARD			(1<<2)
+#define	CM_SIDE					(1<<3)
+#define	CM_UP						(1<<4)
+#define	CM_BUTTONS			(1<<5)
+#define	CM_IMPULSE			(1<<6)
+#define	CM_ANGLE2				(1<<7)
+#define	U_ORIGIN1				(1<<9)
+#define	U_ORIGIN2				(1<<10)
+#define	U_ORIGIN3				(1<<11)
+#define	U_ANGLE2				(1<<12)
+#define	U_FRAME					(1<<13)
+#define	U_REMOVE				(1<<14)		// REMOVE this entity, don't add it
+#define	U_MOREBITS			(1<<15)
+#define	U_ANGLE1				(1<<0)
+#define	U_ANGLE3				(1<<1)
+#define	U_MODEL					(1<<2)
+#define	U_COLORMAP			(1<<3)
+#define	U_SKIN					(1<<4)
+#define	U_EFFECTS				(1<<5)
+#define	U_SOLID					(1<<6)		// the entity should be solid for prediction
+#define	SND_VOLUME			(1<<15)		// a byte
+#define	SND_ATTENUATION	(1<<14)		// a byte
+#define	TE_SPIKE					0
+#define	TE_SUPERSPIKE			1
+#define	TE_GUNSHOT				2
+#define	TE_EXPLOSION			3
+#define	TE_TAREXPLOSION		4
+#define	TE_LIGHTNING1			5
+#define	TE_LIGHTNING2			6
+#define	TE_WIZSPIKE				7
+#define	TE_KNIGHTSPIKE		8
+#define	TE_LIGHTNING3			9
+#define	TE_LAVASPLASH			10
+#define	TE_TELEPORT				11
+#define	TE_BLOOD					12
+#define	TE_LIGHTNINGBLOOD	13
+#define BSPVERSION				29
+#define MAX_MAP_HULLS			4
+
+typedef struct
+{
+	int		fileofs, filelen;
+} lump_t;
+
+#define	LUMP_ENTITIES			0
+#define	LUMP_PLANES				1
+#define	LUMP_TEXTURES			2
+#define	LUMP_VERTEXES			3
+#define	LUMP_VISIBILITY		4
+#define	LUMP_NODES				5
+#define	LUMP_TEXINFO			6
+#define	LUMP_FACES				7
+#define	LUMP_LIGHTING			8
+#define	LUMP_CLIPNODES		9
+#define	LUMP_LEAFS				10
+#define	LUMP_MARKSURFACES 11
+#define	LUMP_EDGES				12
+#define	LUMP_SURFEDGES		13
+#define	LUMP_MODELS				14
+#define	HEADER_LUMPS			15
+
+typedef struct
+{
+	float		mins[3], maxs[3];
+	float		origin[3];
+	int			headnode[MAX_MAP_HULLS];
+	int			visleafs;		// not including the solid leaf 0
+	int			firstface, numfaces;
+} dmodel_t;
+
+typedef struct
+{
+	int				version;
+	lump_t		lumps[HEADER_LUMPS];
+} dheader_t;
+
+#define MAX_MSGLEN 2048
+
+#endif // QUAKEDEF_H

+ 45 - 0
qwclient.pro

@@ -0,0 +1,45 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2012-08-16T04:30:39
+#
+#-------------------------------------------------
+
+QT       += network
+
+QT       -= gui
+
+TARGET = qwclient
+TEMPLATE = lib
+
+DEFINES += QWCLIENT_LIBRARY
+
+SOURCES += QWClient.cpp \
+    QWClientPrivate.cpp \
+    QWPack.cpp \
+    QWTables.cc
+
+HEADERS += QWClient.h\
+        qwclient_global.h \
+    QWClientPrivate.h \
+    quakedef.h \
+    QWPack.h \
+    QWTables.h
+
+symbian {
+    MMP_RULES += EXPORTUNFROZEN
+    TARGET.UID3 = 0xE3787C8C
+    TARGET.CAPABILITY = 
+    TARGET.EPOCALLOWDLLDATA = 1
+    addFiles.sources = qwclient.dll
+    addFiles.path = !:/sys/bin
+    DEPLOYMENT += addFiles
+}
+
+unix:!symbian {
+    maemo5 {
+        target.path = /opt/usr/lib
+    } else {
+        target.path = /usr/lib
+    }
+    INSTALLS += target
+}

+ 31 - 0
qwclient_global.h

@@ -0,0 +1,31 @@
+/*
+GNU General Public License version 3 notice
+
+Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see < http://www.gnu.org/licenses/ >.
+*/
+
+#ifndef QWCLIENT_GLOBAL_H
+#define QWCLIENT_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(QWCLIENT_LIBRARY)
+#  define QWCLIENTSHARED_EXPORT Q_DECL_EXPORT
+#else
+#  define QWCLIENTSHARED_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // QWCLIENT_GLOBAL_H