about summary refs log tree commit diff
diff options
context:
space:
mode:
authoroy <Tom_Adams@web.de>2011-03-31 15:13:49 +0200
committeroy <Tom_Adams@web.de>2011-03-31 15:13:49 +0200
commit2a72c0b38b8c2610001f24340097ad3a1b052263 (patch)
treeaee816df77e1f1ac772d5a797b822d2ffc0c09b5
parent59d56cd332ecc86008c27326631566a4d2d94ecb (diff)
downloadzcatch-2a72c0b38b8c2610001f24340097ad3a1b052263.tar.gz
zcatch-2a72c0b38b8c2610001f24340097ad3a1b052263.zip
added a mechanism to check for a valid standard map. Closes #132
-rw-r--r--src/engine/client/client.cpp63
-rw-r--r--src/engine/client/client.h1
-rw-r--r--src/engine/server/server.cpp8
-rw-r--r--src/engine/server/server.h1
-rw-r--r--src/engine/shared/datafile.cpp26
-rw-r--r--src/engine/shared/datafile.h2
-rw-r--r--src/engine/shared/mapchecker.cpp106
-rw-r--r--src/engine/shared/mapchecker.h38
-rw-r--r--src/tools/map_version.cpp66
-rw-r--r--src/versionsrv/versionsrv.cpp71
-rw-r--r--src/versionsrv/versionsrv.h27
11 files changed, 389 insertions, 20 deletions
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
index 28c0fad4..8353bc0a 100644
--- a/src/engine/client/client.cpp
+++ b/src/engine/client/client.cpp
@@ -26,7 +26,7 @@
 #include <engine/shared/compression.h>
 #include <engine/shared/datafile.h>
 #include <engine/shared/demo.h>
-#include <engine/shared/memheap.h>
+#include <engine/shared/mapchecker.h>
 #include <engine/shared/network.h>
 #include <engine/shared/packer.h>
 #include <engine/shared/protocol.h>
@@ -1023,24 +1023,47 @@ int CClient::PlayerScoreComp(const void *a, const void *b)
 
 void CClient::ProcessConnlessPacket(CNetChunk *pPacket)
 {
-	// version info
-	if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) &&
-		mem_comp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0 &&
-		m_VersionInfo.m_State == CVersionInfo::STATE_READY && net_addr_comp(&pPacket->m_Address, &m_VersionInfo.m_VersionServeraddr.m_Addr) == 0)
+	// version server
+	if(m_VersionInfo.m_State == CVersionInfo::STATE_READY && net_addr_comp(&pPacket->m_Address, &m_VersionInfo.m_VersionServeraddr.m_Addr) == 0)
 	{
-		unsigned char *pVersionData = (unsigned char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION);
-		int VersionMatch = !mem_comp(pVersionData, VERSION_DATA, sizeof(VERSION_DATA));
+		// version info
+		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));
+
+			char aBuf[256];
+			str_format(aBuf, sizeof(aBuf), "version does %s (%d.%d.%d)",
+				VersionMatch ? "match" : "NOT match",
+				pVersionData[1], pVersionData[2], pVersionData[3]);
+			m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/version", aBuf);
 
-		char aBuf[256];
-		str_format(aBuf, sizeof(aBuf), "version does %s (%d.%d.%d)",
-			VersionMatch ? "match" : "NOT match",
-			pVersionData[1], pVersionData[2], pVersionData[3]);
-		m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/version", aBuf);
+			// 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]);
+			}
+
+			// request the map version list now
+			CNetChunk Packet;
+			mem_zero(&Packet, sizeof(Packet));
+			Packet.m_ClientID = -1;
+			Packet.m_Address = m_VersionInfo.m_VersionServeraddr.m_Addr;
+			Packet.m_pData = VERSIONSRV_GETMAPLIST;
+			Packet.m_DataSize = sizeof(VERSIONSRV_GETMAPLIST);
+			Packet.m_Flags = NETSENDFLAG_CONNLESS;
+			m_NetClient.Send(&Packet);
+		}
 
-		// assume version is out of date when version-data doesn't match
-		if (!VersionMatch)
+		// map version list
+		if(pPacket->m_DataSize >= (int)sizeof(VERSIONSRV_MAPLIST) &&
+			mem_comp(pPacket->m_pData, VERSIONSRV_MAPLIST, sizeof(VERSIONSRV_MAPLIST)) == 0)		
 		{
-			str_format(m_aVersionStr, sizeof(m_aVersionStr), "%d.%d.%d", pVersionData[1], pVersionData[2], pVersionData[3]);
+			int Size = pPacket->m_DataSize-sizeof(VERSIONSRV_MAPLIST);
+			int Num = Size/sizeof(CMapVersion);
+			m_MapChecker.AddMaplist((CMapVersion *)((char*)pPacket->m_pData+sizeof(VERSIONSRV_MAPLIST)), Num);
 		}
 	}
 
@@ -1073,9 +1096,9 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket)
 			NETADDR Addr;
 
 			// copy address
-			Addr.type = (pAddrs->m_aType[0]<<24) | (pAddrs->m_aType[1]<<16) | (pAddrs->m_aType[2]<<8) | pAddrs->m_aType[3];
-			mem_copy(Addr.ip, pAddrs->m_aIp, sizeof(Addr.ip));
-			Addr.port = (pAddrs->m_aPort[0]<<8) | pAddrs->m_aPort[1];
+			Addr.type = (pAddrs[i].m_aType[0]<<24) | (pAddrs[i].m_aType[1]<<16) | (pAddrs[i].m_aType[2]<<8) | pAddrs[i].m_aType[3];
+			mem_copy(Addr.ip, pAddrs[i].m_aIp, sizeof(Addr.ip));
+			Addr.port = (pAddrs[i].m_aPort[0]<<8) | pAddrs[i].m_aPort[1];
 			
 			m_ServerBrowser.Set(Addr, IServerBrowser::SET_MASTER_ADD, -1, 0x0);
 		}
@@ -1159,6 +1182,10 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)
 			if(Unpacker.Error())
 				return;
 
+			// check for valid standard map
+			if(!m_MapChecker.IsMapValid(pMap, MapCrc, MapSize))
+				pError = "invalid standard map";
+
 			for(int i = 0; pMap[i]; i++) // protect the player from nasty map names
 			{
 				if(pMap[i] == '/' || pMap[i] == '\\')
diff --git a/src/engine/client/client.h b/src/engine/client/client.h
index b0696b96..86c56e0a 100644
--- a/src/engine/client/client.h
+++ b/src/engine/client/client.h
@@ -106,6 +106,7 @@ class CClient : public IClient, public CDemoPlayer::IListner
 	class CDemoRecorder m_DemoRecorder;
 	class CServerBrowser m_ServerBrowser;
 	class CFriends m_Friends;
+	class CMapChecker m_MapChecker;
 
 	char m_aServerAddressStr[256];
 
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
index f1d2e6e1..f170b1c8 100644
--- a/src/engine/server/server.cpp
+++ b/src/engine/server/server.cpp
@@ -15,6 +15,7 @@
 #include <engine/shared/config.h>
 #include <engine/shared/datafile.h>
 #include <engine/shared/demo.h>
+#include <engine/shared/mapchecker.h>
 #include <engine/shared/network.h>
 #include <engine/shared/packer.h>
 #include <engine/shared/protocol.h>
@@ -1049,6 +1050,13 @@ int CServer::LoadMap(const char *pMapName)
 	/*df = datafile_load(buf);
 	if(!df)
 		return 0;*/
+
+	// check for valid standard map
+	if(!m_MapChecker.ReadAndValidateMap(Storage(), aBuf, IStorage::TYPE_ALL))
+	{
+		Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "mapchecker", "invalid standard map");
+		return 0;
+	}
 		
 	if(!m_pMap->Load(aBuf))
 		return 0;
diff --git a/src/engine/server/server.h b/src/engine/server/server.h
index 00c1c648..abd86f92 100644
--- a/src/engine/server/server.h
+++ b/src/engine/server/server.h
@@ -121,6 +121,7 @@ public:
 	
 	CDemoRecorder m_DemoRecorder;
 	CRegister m_Register;
+	CMapChecker m_MapChecker;
 	
 	CServer();
 	
diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp
index 74583ab8..74936ea0 100644
--- a/src/engine/shared/datafile.cpp
+++ b/src/engine/shared/datafile.cpp
@@ -222,6 +222,32 @@ bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename, int
 	return true;
 }
 
+bool CDataFileReader::GetCrcSize(class IStorage *pStorage, const char *pFilename, int StorageType, unsigned *pCrc, unsigned *pSize)
+{
+	IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType);
+	if(!File)
+		return false;
+	
+	// get crc and size
+	unsigned Crc = 0;
+	unsigned Size = 0;
+	unsigned char aBuffer[64*1024];
+	while(1)
+	{
+		unsigned Bytes = io_read(File, aBuffer, sizeof(aBuffer));
+		if(Bytes <= 0)
+			break;
+		Crc = crc32(Crc, aBuffer, Bytes); // ignore_convention
+		Size += Bytes;
+	}
+	
+	io_close(File);
+
+	*pCrc = Crc;
+	*pSize = Size;
+	return true;
+}
+
 int CDataFileReader::NumData()
 {
 	if(!m_pDataFile) { return 0; }
diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h
index ce7b11aa..15bed033 100644
--- a/src/engine/shared/datafile.h
+++ b/src/engine/shared/datafile.h
@@ -16,6 +16,8 @@ public:
 	
 	bool Open(class IStorage *pStorage, const char *pFilename, int StorageType);
 	bool Close();
+
+	static bool GetCrcSize(class IStorage *pStorage, const char *pFilename, int StorageType, unsigned *pCrc, unsigned *pSize);
 	
 	void *GetData(int Index);
 	void *GetDataSwapped(int Index); // makes sure that the data is 32bit LE ints when saved
diff --git a/src/engine/shared/mapchecker.cpp b/src/engine/shared/mapchecker.cpp
new file mode 100644
index 00000000..dec4271d
--- /dev/null
+++ b/src/engine/shared/mapchecker.cpp
@@ -0,0 +1,106 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com.                */
+#include <base/math.h>
+#include <base/system.h>
+
+#include <engine/storage.h>
+
+#include <versionsrv/versionsrv.h>
+
+#include "datafile.h"
+#include "memheap.h"
+#include "mapchecker.h"
+
+CMapChecker::CMapChecker()
+{
+	Init();
+	SetDefaults();
+}
+
+void CMapChecker::Init()
+{
+	m_Whitelist.Reset();
+	m_pFirst = 0;
+	m_RemoveDefaultList = false;
+}
+
+void CMapChecker::SetDefaults()
+{
+	AddMaplist(s_aMapVersionList, s_NumMapVersionItems);
+	m_RemoveDefaultList = true;
+}
+
+void CMapChecker::AddMaplist(CMapVersion *pMaplist, int Num)
+{
+	if(m_RemoveDefaultList)
+		Init();
+
+	for(int i = 0; i < Num; ++i)
+	{
+		CWhitelistEntry *pEntry = (CWhitelistEntry *)m_Whitelist.Allocate(sizeof(CWhitelistEntry));
+		pEntry->m_pNext = m_pFirst;
+		m_pFirst = pEntry;
+
+		str_copy(pEntry->m_aMapName, pMaplist[i].m_aName, sizeof(pEntry->m_aMapName));
+		pEntry->m_MapCrc = (pMaplist[i].m_aCrc[0]<<24) | (pMaplist[i].m_aCrc[1]<<16) | (pMaplist[i].m_aCrc[2]<<8) | pMaplist[i].m_aCrc[3]; 
+		pEntry->m_MapSize = (pMaplist[i].m_aSize[0]<<24) | (pMaplist[i].m_aSize[1]<<16) | (pMaplist[i].m_aSize[2]<<8) | pMaplist[i].m_aSize[3];
+	}
+}
+
+bool CMapChecker::IsMapValid(const char *pMapName, unsigned MapCrc, unsigned MapSize)
+{
+	bool StandardMap = false;
+	for(CWhitelistEntry *pCurrent = m_pFirst; pCurrent; pCurrent = pCurrent->m_pNext)
+	{
+		if(str_comp(pCurrent->m_aMapName, pMapName) == 0)
+		{
+			StandardMap = true;
+			if(pCurrent->m_MapCrc == MapCrc && pCurrent->m_MapSize == MapSize)
+				return true;
+		}
+	}
+	return StandardMap?false:true;
+}
+
+bool CMapChecker::ReadAndValidateMap(IStorage *pStorage, const char *pFilename, int StorageType)
+{
+	bool LoadedMapInfo = false;
+	bool StandardMap = false;
+	unsigned MapCrc = 0;
+	unsigned MapSize = 0;
+
+	// extract map name
+	char aMapName[MAX_MAP_LENGTH];
+	const char *pExtractedName = pFilename;
+	const char *pEnd = 0;
+	for(const char *pSrc = pFilename; *pSrc; ++pSrc)
+	{
+		if(*pSrc == '/' || *pSrc == '\\')
+			pExtractedName = pSrc+1;
+		else if(*pSrc == '.')
+			pEnd = pSrc;
+	}
+	int Length = (int)(pEnd - pExtractedName);
+	if(Length <= 0 || Length >= MAX_MAP_LENGTH)
+		return true;
+	str_copy(aMapName, pExtractedName, min((int)MAX_MAP_LENGTH, (int)(pEnd-pExtractedName+1)));
+
+	// check for valid map
+	for(CWhitelistEntry *pCurrent = m_pFirst; pCurrent; pCurrent = pCurrent->m_pNext)
+	{
+		if(str_comp(pCurrent->m_aMapName, aMapName) == 0)
+		{
+			StandardMap = true;
+			if(!LoadedMapInfo)
+			{
+				if(!CDataFileReader::GetCrcSize(pStorage, pFilename, StorageType, &MapCrc, &MapSize))
+					return true;
+				LoadedMapInfo = true;
+			}
+
+			if(pCurrent->m_MapCrc == MapCrc && pCurrent->m_MapSize == MapSize)
+				return true;
+		}
+	}
+	return StandardMap?false:true;
+}
diff --git a/src/engine/shared/mapchecker.h b/src/engine/shared/mapchecker.h
new file mode 100644
index 00000000..0d4332d9
--- /dev/null
+++ b/src/engine/shared/mapchecker.h
@@ -0,0 +1,38 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com.                */
+#ifndef ENGINE_SHARED_MAPCHECKER_H
+#define ENGINE_SHARED_MAPCHECKER_H
+
+#include "memheap.h"
+
+class CMapChecker
+{
+	enum
+	{
+		MAX_MAP_LENGTH=8,
+	};
+
+	struct CWhitelistEntry
+	{
+		char m_aMapName[MAX_MAP_LENGTH];
+		unsigned m_MapCrc;
+		unsigned m_MapSize;
+		CWhitelistEntry *m_pNext;
+	};
+	
+	class CHeap m_Whitelist;
+	CWhitelistEntry *m_pFirst;
+
+	bool m_RemoveDefaultList;
+
+	void Init();
+	void SetDefaults();
+
+public:
+	CMapChecker();
+	void AddMaplist(class CMapVersion *pMaplist, int Num);
+	bool IsMapValid(const char *pMapName, unsigned MapCrc, unsigned MapSize);
+	bool ReadAndValidateMap(class IStorage *pStorage, const char *pFilename, int StorageType);
+};
+
+#endif
diff --git a/src/tools/map_version.cpp b/src/tools/map_version.cpp
new file mode 100644
index 00000000..d995f478
--- /dev/null
+++ b/src/tools/map_version.cpp
@@ -0,0 +1,66 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com.                */
+#include <base/math.h>
+#include <base/system.h>
+
+#include <engine/kernel.h>
+#include <engine/map.h>
+#include <engine/storage.h>
+
+
+static IOHANDLE s_File = 0;
+static IStorage *s_pStorage = 0;
+static IEngineMap *s_pEngineMap = 0;
+
+int MaplistCallback(const char *pName, int IsDir, int DirType, void *pUser)
+{
+	int l = str_length(pName);
+	if(l < 4 || IsDir || str_comp(pName+l-4, ".map") != 0)
+		return 0;
+
+	char aBuf[128];
+	str_format(aBuf, sizeof(aBuf), "maps/%s", pName);
+	if(!s_pEngineMap->Load(aBuf))
+		return 0;
+
+	unsigned MapCrc = s_pEngineMap->Crc();
+	s_pEngineMap->Unload();
+
+	IOHANDLE MapFile = s_pStorage->OpenFile(aBuf, IOFLAG_READ, DirType);
+	unsigned MapSize = io_length(MapFile);
+	io_close(MapFile);
+	
+	char aMapName[8];
+	str_copy(aMapName, pName, min((int)sizeof(aMapName),l-3));
+
+	str_format(aBuf, sizeof(aBuf), "\t{\"%s\", {0x%02x, 0x%02x, 0x%02x, 0x%02x}, {0x%02x, 0x%02x, 0x%02x, 0x%02x}},\n", aMapName,
+		(MapCrc>>24)&0xff, (MapCrc>>16)&0xff, (MapCrc>>8)&0xff, MapCrc&0xff,
+		(MapSize>>24)&0xff, (MapSize>>16)&0xff, (MapSize>>8)&0xff, MapSize&0xff);
+	io_write(s_File, aBuf, str_length(aBuf));
+
+	return 0;
+}
+
+int main(int argc, const char **argv) // ignore_convention
+{
+	IKernel *pKernel = IKernel::Create();
+	s_pStorage = CreateStorage("Teeworlds", argc, argv);
+	s_pEngineMap = CreateEngineMap();
+
+	bool RegisterFail = !pKernel->RegisterInterface(s_pStorage);
+	RegisterFail |= !pKernel->RegisterInterface(s_pEngineMap);
+
+	if(RegisterFail)
+		return -1;
+	
+	s_File = s_pStorage->OpenFile("map_version.txt", IOFLAG_WRITE, 1);
+	if(s_File)
+	{
+		io_write(s_File, "static CMapVersion s_aMapVersionList[] = {\n", str_length("static CMapVersion s_aMapVersionList[] = {\n"));
+		s_pStorage->ListDirectory(1, "maps", MaplistCallback, 0);
+		io_write(s_File, "};\n", str_length("};\n"));
+		io_close(s_File);
+	}
+	
+	return 0;
+}
diff --git a/src/versionsrv/versionsrv.cpp b/src/versionsrv/versionsrv.cpp
index 1ca3835f..d36fba3b 100644
--- a/src/versionsrv/versionsrv.cpp
+++ b/src/versionsrv/versionsrv.cpp
@@ -6,8 +6,54 @@
 
 #include "versionsrv.h"
 
+enum {
+	MAX_MAPS_PER_PACKET=48,
+	MAX_PACKETS=16,
+	MAX_MAPS=MAX_MAPS_PER_PACKET*MAX_PACKETS,
+};
+
+struct CPacketData
+{
+	int m_Size;
+	struct {
+		unsigned char m_aHeader[sizeof(VERSIONSRV_MAPLIST)];
+		CMapVersion m_aMaplist[MAX_MAPS_PER_PACKET];
+	} m_Data;
+};
+
+CPacketData m_aPackets[MAX_PACKETS];
+static int m_NumPackets = 0;
+
 static CNetClient g_NetOp; // main
 
+void BuildPackets()
+{
+	CMapVersion *pCurrent = &s_aMapVersionList[0];
+	int ServersLeft = s_NumMapVersionItems;
+	m_NumPackets = 0;
+	while(ServersLeft && m_NumPackets < MAX_PACKETS)
+	{
+		int Chunk = ServersLeft;
+		if(Chunk > MAX_MAPS_PER_PACKET)
+			Chunk = MAX_MAPS_PER_PACKET;
+		ServersLeft -= Chunk;
+		
+		// copy header	
+		mem_copy(m_aPackets[m_NumPackets].m_Data.m_aHeader, VERSIONSRV_MAPLIST, sizeof(VERSIONSRV_MAPLIST));
+		
+		// copy map versions
+		for(int i = 0; i < Chunk; i++)
+		{
+			m_aPackets[m_NumPackets].m_Data.m_aMaplist[i] = *pCurrent;
+			pCurrent++;
+		}
+		
+		m_aPackets[m_NumPackets].m_Size = sizeof(VERSIONSRV_MAPLIST) + sizeof(CMapVersion)*Chunk;
+		
+		m_NumPackets++;
+	}
+}
+
 void SendVer(NETADDR *pAddr)
 {
 	CNetChunk p;
@@ -33,8 +79,15 @@ int main(int argc, char **argv) // ignore_convention
 	net_init();
 
 	mem_zero(&BindAddr, sizeof(BindAddr));
+	BindAddr.type = NETTYPE_ALL;
 	BindAddr.port = VERSIONSRV_PORT;
-	g_NetOp.Open(BindAddr, 0);
+	if(!g_NetOp.Open(BindAddr, 0))
+	{
+		dbg_msg("mastersrv", "couldn't start network");
+		return -1;
+	}
+
+	BuildPackets();
 	
 	dbg_msg("versionsrv", "started");
 	
@@ -51,6 +104,22 @@ int main(int argc, char **argv) // ignore_convention
 			{
 				SendVer(&Packet.m_Address);
 			}
+
+			if(Packet.m_DataSize == sizeof(VERSIONSRV_GETMAPLIST) &&
+				mem_comp(Packet.m_pData, VERSIONSRV_GETMAPLIST, sizeof(VERSIONSRV_GETMAPLIST)) == 0)
+			{
+				CNetChunk p;
+				p.m_ClientID = -1;
+				p.m_Address = Packet.m_Address;
+				p.m_Flags = NETSENDFLAG_CONNLESS;
+				
+				for(int i = 0; i < m_NumPackets; i++)
+				{
+					p.m_DataSize = m_aPackets[i].m_Size;
+					p.m_pData = &m_aPackets[i].m_Data;
+					g_NetOp.Send(&p);
+				}
+			}
 		}
 		
 		// be nice to the CPU
diff --git a/src/versionsrv/versionsrv.h b/src/versionsrv/versionsrv.h
index 68ba04ca..03ca2f21 100644
--- a/src/versionsrv/versionsrv.h
+++ b/src/versionsrv/versionsrv.h
@@ -4,8 +4,33 @@
 #define VERSIONSRV_VERSIONSRV_H
 static const int VERSIONSRV_PORT = 8302;
 
-static const unsigned char VERSION_DATA[] = {0x00, 0, 5, 1}; // TODO 0.6: fix me
+struct CMapVersion
+{
+	char m_aName[8];
+	unsigned char m_aCrc[4];
+	unsigned char m_aSize[4];
+};
+
+static CMapVersion s_aMapVersionList[] = {
+	{"ctf1", {0xd2, 0xa8, 0x20, 0xbd}, {0x00, 0x00, 0x11, 0x9b}},
+	{"ctf2", {0xb2, 0x19, 0xe7, 0x13}, {0x00, 0x00, 0x63, 0xfc}},
+	{"ctf3", {0xe8, 0xd3, 0x39, 0xd0}, {0x00, 0x00, 0x11, 0x0e}},
+	{"ctf4", {0x09, 0xd1, 0x70, 0x1d}, {0x00, 0x00, 0x20, 0x3f}},
+	{"ctf5", {0x60, 0x7e, 0x6d, 0x7e}, {0x00, 0x00, 0x2f, 0x51}},
+	{"dm1", {0xf3, 0x5c, 0x93, 0x09}, {0x00, 0x00, 0x15, 0x75}},
+	{"dm2", {0x81, 0xe0, 0xa8, 0x9e}, {0x00, 0x00, 0x20, 0x11}},
+	{"dm6", {0xae, 0x83, 0x06, 0x9f}, {0x00, 0x00, 0x17, 0x1d}},
+	{"dm7", {0x42, 0x6d, 0xa1, 0x67}, {0x00, 0x00, 0x27, 0x2a}},
+	{"dm8", {0x92, 0x23, 0x91, 0x82}, {0x00, 0x00, 0x3a, 0x33}},
+	{"dm9", {0x48, 0xca, 0xfd, 0x11}, {0x00, 0x00, 0x18, 0x94}},
+};
+static int s_NumMapVersionItems = sizeof(s_aMapVersionList)/sizeof(CMapVersion);
+
+static const unsigned char VERSION_DATA[] = {0x00, 0, 6, 0};
 
 static const unsigned char VERSIONSRV_GETVERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 'g'};
 static const unsigned char VERSIONSRV_VERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 's'};
+
+static const unsigned char VERSIONSRV_GETMAPLIST[] = {255, 255, 255, 255, 'v', 'm', 'l', 'g'};
+static const unsigned char VERSIONSRV_MAPLIST[] = {255, 255, 255, 255, 'v', 'm', 'l', 's'};
 #endif