示例#1
0
void Network_Manager::Initialize() {
	logWrite("Initializing networking...");
	Available = true;

	#ifdef _WIN32
	    WSADATA wsaData;
	    WORD wVersionRequested = MAKEWORD(2, 0);

		int result = WSAStartup(wVersionRequested, &wsaData);
	    if (result != 0) {
			logWrite("Win32 networking failed (error %d)",result);
			Available = false;
		} else {
			logWritem("network: %s",wsaData.szDescription);
		}
	#else
		logWritem("network: unix sockets");
	#endif

	Connections.Preallocate(256);
	maxMessageSize = 1024;

	//Convars
	cvMaxMessageSize = Convar.Create("net_maxsize",cvMaxMessageSize_Read,cvMaxMessageSize_Write);
	cvMaxConnections = Convar.Create("net_maxconnections",cvMaxConnections_Read,cvMaxConnections_Write);
	cvSendQueueSize = Convar.Create("net_send_queue",128);
	cvTimeRate = Convar.Create("net_timerate",0.1f);
	cvFakeLag = Convar.Create("net_fakelag",0.0f);

	remainingTimeSyncMsgs = 0;
	prevTimeSyncMessageTime = 0;

	IsServer = false;
	IsConnected = false;

	networkCallback[NETMESSAGE_SERVERINFO] = netCallback_SERVERINFO;
	networkCallback[NETMESSAGE_TIMESYNC] = netCallback_TIMESYNC;

	//Initialize stats
	numBytesSent = 0;
	numBytesReceived = 0;
	numPacketsSent = 0;
	numPacketsReceived = 0;

	prevTimeSyncTick = 0;
	msgSendLock = 0;
};
示例#2
0
int Network_Connection::Send(void* buf, int size) {
	if (socketHandle >= 0) {
		int bytes = send(socketHandle, (char*)buf, size, 0);
		if (bytes < 0) {
			#ifdef WIN32
			logWritem("network: socket write error %d, dropping (socket: %d)", GetLastError(), socketHandle);
			#else
			logWritem("network: socket write error %d, dropping (socket: %d)", errno, socketHandle);
			#endif
			Close();
			return 0;
		}
		Network.numBytesSent += bytes;
		return bytes;
	}
	return 0;
}
示例#3
0
void Map_Manager::cellUncache(int cx, int cy) {
	LockID lockID = Thread.EnterLock(MUTEX_MAP);
		int idx = cellCacheIndex(cx,cy);
		if (idx == BAD_ID) {
			logWritem("cellUncache: map [%d;%d] is not cached",cx,cy);
			Thread.LeaveLock(lockID);
			return;
		}
	
		releaseMap(*cacheCityscape[idx]);
		cacheCityscape.Count--;
		if (cacheCityscape.Count > 0) {
			*cacheCityscape[idx] = *cacheCityscape[cacheCityscape.Count];
		}
		logWritem("cellUncache: map [%d;%d] removed from cache",cx,cy);
	Thread.LeaveLock(lockID);
}
示例#4
0
文件: anim.cpp 项目: basecq/OpenGTA2
AnimSeqID Animation_Manager::GetAnimationSeq(const char* name) {
	int nameLen = strlen(name);
	for (uint i = 0; i < Entries.Count; i++) {
		if (strncmp(Entries[i]->Name,name,nameLen) == 0) return i;
	}
	logWritem("Animation not found: %s",name);
	return BAD_ID;
}
示例#5
0
文件: anim.cpp 项目: basecq/OpenGTA2
void Animation_Manager::LoadFromChunk(Chunk_Loader* TEX) {
	if (!TEX->IsChunk("ANIM")) return;

	if (!TEX->IsEndOfChunk()) {
		logWritem("Loading animations...");
	} else {
		logWritem("Warning: animation chunk is empty");
	}
	
	while (!TEX->IsEndOfChunk()) {
		char sprStartName[256]; 
		char sprEndName[256];
		char animName[256];
		float animFPS;
		char animType;

		TEX->ReadString(animName);
		TEX->Read(&animType,1);

		logWritem("\t%s [type: %.2x]",animName,animType);

		animation_entry* anim = Animations.Entries.Add();
		anim->Type = animType;
		anim->Name = mem.alloc_string(strlen(animName)+1);
		strcpy(anim->Name,animName);

		switch (animType) { //
			case ANIMTYPE_FORWARDLOOP:
			case ANIMTYPE_FORWARDONCE:
				TEX->ReadString(sprStartName);
				TEX->ReadString(sprEndName);
				TEX->Read(&animFPS,4);
				anim->FPS = animFPS;

				anim->Sprites = (TexID*)mem.alloc(2*sizeof(TexID));
				anim->Sprites[0] = Graphics.GetTextureID(sprStartName);
				anim->Sprites[1] = Graphics.GetTextureID(sprEndName);
				anim->numSprites = 2;
				break;
			default:
				logError("Animation: unsupported type %d",animType);
				return;
		}
	}
}
示例#6
0
void debugTestDebug() {
	logWrite("Testing debugging...");
	logError("This line should trigger a breakpoint");
	assert(true);
	assert(false);
	logWrite("Log entry");
	logWritem("Log debug entry");

	logWrite("Debug test passed!\n");
}
示例#7
0
TexID Graphics_Manager::GetTextureID(const char* name) {
	int nameLen = strlen(name);
	if (nameLen == 0) return BAD_ID;
	for (uint i = 0; i < graphicsEntries.Count; i++) {
		for (uint j = 0; j < graphicsEntries[i]->TextureAtlas->Textures.Count; j++) {
			if (strcmp(name,graphicsEntries[i]->TextureAtlas->Textures[j]->textureName) == 0) {
				return graphicsEntries[i]->TextureAtlas->FirstID + j;
			}
		}
	}
	logWritem("Sprite not found: %s",name);
	return BAD_ID;
}
示例#8
0
int Network_Connection::Recv(void* buf, int size) {
	if (socketHandle >= 0) {
		if (!canReceive()) return 0;

		#ifdef WIN32
			int bytes = recv(socketHandle, (char*)buf, size, 0);
		#else
			int bytes = recv(socketHandle, buf, size, MSG_NOSIGNAL | MSG_DONTWAIT);
		#endif

		if (bytes <= 0) { //FIXME: proper error handling
			#ifdef WIN32
			logWritem("network: socket read error %d, dropping (socket: %d)", GetLastError(), socketHandle);
			#else
			logWritem("network: socket read error %d, dropping (socket: %d)", errno, socketHandle);
			#endif
			bytes = 0;
			Close();
		}
		Network.numBytesReceived += bytes;
		return bytes;
	} else return -1;
}
示例#9
0
void Map_Render_Debug::Initialize() {
    //glShadeModel(GL_SMOOTH);
    //glEnable(GL_LIGHTING);
	//glEnable(GL_NORMALIZE);
    //glEnable(GL_TEXTURE_2D);
    //glEnable(GL_CULL_FACE);

	nextAnimationTime = 0.0f;
	VBOEntry.Preallocate(4);
	for (uint i = 0; i < VBOEntry.AllocCount; i++) {
		logWritem("creating vertex buffer %d",i);
		VBOEntry[i]->VBO.Create(32768*3);//131072);//0);
	}
	//FIXME: VBOs take A LOT of space
}
示例#10
0
void Network_Connection::Update() {
	if (listenConnection) {
		struct sockaddr_in fromAddress;
		memset(&fromAddress, 0, sizeof(fromAddress));
		#ifdef _WIN32
			int fromLen = sizeof(fromAddress);
		#else
			socklen_t fromLen = sizeof(fromAddress);
		#endif
	
	    if (canReceive()) {
			int clientHandle = accept(socketHandle, (struct sockaddr*)&fromAddress, &fromLen);
			if (clientHandle < 0) return;
	
			Network_Connection* clientConnection = Network.NewConnection();
			if (!clientConnection) return;
			clientConnection->Open(0,0); //Initialize resources
			clientConnection->peerAddress = fromAddress;
			clientConnection->socketHandle = clientHandle;

			#ifdef WIN32
			logWritem("network: incoming connection %.8X:%d (socket: %d, cid %d)",
				fromAddress.sin_addr.S_un.S_addr, htons(fromAddress.sin_port), clientHandle, Network.Connections.Count - 1);
			#else
			logWritem("network: incoming connection %.8X:%d (socket: %d, cid %d)",
				fromAddress.sin_addr.s_addr, htons(fromAddress.sin_port), clientHandle, Network.Connections.Count - 1);
			#endif
		}
	} else {
		LockID lockID = Thread.EnterLock(MUTEX_NETWORK_RECVBUF);

		//A slightly hacky message receiver
		//msgSize is set to how big message currently should be
		//addSize is set after first byte was fetched, and says how much extra data needs to be fetched
		//        before msgSize will be reset from 1 (default) to actual message size (excluding additional data)
		int readSize = 3;
		while (canReceive() || (recvMessage.rawSize >= readSize))  {
			if (recvMessage.rawSize > 0) { //Message already carries message ID
				unsigned short msgSize = *(unsigned short*)((char*)recvMessage.rawBuffer+0);
				unsigned char msgID = *((char*)recvMessage.rawBuffer+2);
				if (msgSize > Network.maxMessageSize) msgSize = Network.maxMessageSize;
				readSize = msgSize;
				if (readSize == 0) return; //FIXME

				if (recvMessage.rawSize >= readSize) {
					if ((msgID < NETMESSAGE_LAST) && (Network.networkCallback[msgID])) {
						Network.numPacketsReceived++;
						recvMessage.rawPos = 3; //Skip message ID and size
						Network.networkCallback[msgID](this,&recvMessage);
						recvMessage.rawSize = 0;
						readSize = 3; //get ready to recv more messages
					} else { //Unhandled message, ignore
						recvMessage.rawSize = 0;
					}
				}
			}
			//read more bytes to the message
			if (recvMessage.rawSize < readSize) {
				recvMessage.rawSize += Recv((recvMessage.rawBuffer+recvMessage.rawSize),readSize-recvMessage.rawSize);
			}
		}

		Thread.LeaveLock(lockID);
	}
}
示例#11
0
bool Network_Connection::Open(char const* host, int port) {
	if (socketHandle >= 0) Close();
	LockID lockID1 = Thread.EnterLock(MUTEX_NETWORK_SENDBUF);
	LockID lockID2 = Thread.EnterLock(MUTEX_NETWORK_RECVBUF);

	sendQueue.Preallocate(Convar[Network.cvSendQueueSize]->GetInt());

	//FIXME: message size in convar
	char* msgBuffer = (char*)mem.alloc(Network.maxMessageSize*(sendQueue.AllocCount+1));
	for (uint i = 0; i < sendQueue.AllocCount; i++) {
		sendQueue[i]->rawBuffer = (msgBuffer + Network.maxMessageSize*i);
		sendQueue[i]->rawSize = 0;
		sendQueue[i]->networkConnection = 0;
	}
	recvMessage.rawBuffer = (msgBuffer + Network.maxMessageSize*sendQueue.AllocCount);
	recvMessage.rawSize = 0;
	recvMessage.rawPos = 0;
	recvMessage.networkConnection = 0;
	connectionOpen = true;

	//Only initialize internals
	if ((!host) && (!port)) {
		Thread.LeaveLock(lockID1);
		Thread.LeaveLock(lockID2);
		return true;
	}

	peerAddress.sin_family = AF_INET;
	peerAddress.sin_port = htons(port);
	if (host) {
		peerAddress.sin_addr.s_addr = inet_addr(host);
		if (peerAddress.sin_addr.s_addr == (unsigned int)-1) {
			//struct hostent *he = gethostbyname(host);
			///if (he) {
			//	peerAddress.sin_addr.s_addr = ((struct in_addr*)*he->h_addr_list)->s_addr;
			//} else {
				Thread.LeaveLock(lockID1);
				Thread.LeaveLock(lockID2);
				Close();
				return false;
			//}
		}
	} else {
		peerAddress.sin_addr.s_addr = htonl(INADDR_ANY);
	}

	socketHandle = socket(AF_INET, SOCK_STREAM, 0);
	if (socketHandle < 0) {
		Thread.LeaveLock(lockID1);
		Thread.LeaveLock(lockID2);
		Close();
		return false;
	}

	if (!host) {
		int one = 1;
		setsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one));

		if (bind(socketHandle, (struct sockaddr*)&peerAddress, sizeof(peerAddress)) < 0) { 
			Thread.LeaveLock(lockID1);
			Thread.LeaveLock(lockID2);
			Close();
			return false;
		}

		if (listen(socketHandle, 20) < 0) {
			Thread.LeaveLock(lockID1);
			Thread.LeaveLock(lockID2);
			Close();
			return false;
		}

		#ifdef WIN32
		logWritem("network: starting server on %.8x:%d (backlog %d)",
			peerAddress.sin_addr.S_un.S_addr, htons(peerAddress.sin_port), 20);
		#else
		logWritem("network: starting server on %.8x:%d (backlog %d)",
			peerAddress.sin_addr.s_addr, htons(peerAddress.sin_port), 20);
		#endif

		listenConnection = true;
	} else {
		if (connect(socketHandle, (struct sockaddr*)&peerAddress, sizeof(peerAddress)) < 0) { 
			Thread.LeaveLock(lockID1);
			Thread.LeaveLock(lockID2);
			Close();
			return false; 
		}
		listenConnection = false;
	}

	#ifdef WIN32
		unsigned long int nonblock = 1;
		ioctlsocket(socketHandle, FIONBIO, &nonblock);
	#else
		// XXX Locks aren't released o_O
		int flags = fcntl(socketHandle, F_GETFL, 0);
		if (0 > flags) {
			logWritem("Unexpected condition in %s:%d", __FILE__, __LINE__);
			return false;
		}

		if (0 > fcntl(socketHandle, F_SETFL, flags | O_NONBLOCK)) {
			logWritem("Unexpected condition in %s:%d", __FILE__, __LINE__);
			return false;
		}
	#endif

	Thread.LeaveLock(lockID1);
	Thread.LeaveLock(lockID2);
	return true;
}
示例#12
0
void Graphics_Manager::texReadFonts(Chunk_Loader* TEX, bool scanOnly) {
	if (TEX->IsChunk("FNT0")) {
		unsigned short fontCount;
		TEX->Read(&fontCount,2);
		
		//Read graphics name (for font GFX)
		for (int f = 0; f < fontCount; f++) {
			char fontName[256];
			unsigned short fontCharCount;

			//Read font name
			TEX->ReadString(fontName);

			//Make new font entry (or append to existing one)
			font_entry* font = 0;
			bool newEntry = false;
			for (uint i = 0; i < Fonts.fontEntries.Count; i++) {
				if (strncmp(Fonts.fontEntries[i]->FontName,fontName,strlen(fontName)) == 0) {
					font = Fonts.fontEntries[i];
					break;
				}
			}
			if (!font) { //Do we need to add a new entry?
				font = Fonts.fontEntries.Add();
				font->FontName = mem.alloc_string(strlen(fontName)+1);
				strcpy(font->FontName,fontName);
				newEntry = true;
			}

			//Initialize font data
			TEX->Read(&fontCharCount,2);	

			char* memBuf = (char*)mem.alloc(fontCharCount*sizeof(TexID) + fontCharCount*sizeof(char*));
			font->characterTextureID = (TexID*)(memBuf);
			font->characterTextureName = (char**)(memBuf+fontCharCount*sizeof(TexID));
			for (int i = 0; i < fontCharCount; i++) {
				font->characterTextureName[i] = 0;
				font->characterTextureID[i] = BAD_ID;
			}

			font->firstCharacter = 0;
			font->numCharacters = fontCharCount;

			if (newEntry) {
				logWritem("Loading font %s (%d characters)...",fontName,fontCharCount);
			} else {
				logWritem("Appending to font %s...",fontName);
			}
			
			//Load all characters
			for (int i = 0; i < fontCharCount; i++) {
				char characterName[256];
				TEX->ReadString(characterName);

				if (characterName[0]) {
					//Allocate name for this texture, if it wasn't yet defined
					if (font->characterTextureName[i] == 0) {
						font->characterTextureName[i] = mem.alloc_string(strlen(characterName)+1);
					} else { //See if there is enough name length to overwrite it
						if (strlen(characterName) > strlen(font->characterTextureName[i])) {
							//FIXME: this leaks memory in a way - there is no way to unalloc string
							font->characterTextureName[i] = mem.alloc_string(strlen(characterName)+1);
						}
					}

					//Copy new character texture name
					strcpy(font->characterTextureName[i],characterName);

					font->characterTextureName[i][strlen(characterName)] = 0;
					font->characterTextureID[i] = Graphics.GetTextureID(font->characterTextureName[i]);
				}
			}
		}
	}
}
示例#13
0
void Graphics_Manager::texReadGTA2Graphics(Chunk_Loader* TEX, bool scanOnly) {
	if (TEX->IsChunk("TILE") || //GTA2-compatible tiles and sprites
		TEX->IsChunk("SPR0")) {
			char graphicsName[256];
			TEX->ReadString(graphicsName);

			logWritem("Found graphics set [%s], FirstID %d",graphicsName,globalIDCounter);

			graphics_entry* entry = 0;
			bool newEntry = false;
			for (uint i = 0; i < graphicsEntries.Count; i++) {
				if (strncmp(graphicsEntries[i]->GraphicName,graphicsName,256) == 0) {
					entry = graphicsEntries[i];
					break;
				}
			}
			if (!entry) { //Do we need to add a new entry?
				entry = graphicsEntries.Add();
				entry->TextureAtlas = (Texture_Atlas*)mem.alloc(sizeof(Texture_Atlas));
				entry->TextureAtlas->Create();
				entry->TextureAtlas->FirstID = globalIDCounter;

				entry->Filename = mem.alloc_string(strlen(TEX->fileName)+1);
				entry->GraphicName = mem.alloc_string(strlen(graphicsName)+1);
				strcpy(entry->Filename,TEX->fileName); //FIXME: has to use TEX filename
				strcpy(entry->GraphicName,graphicsName);
				newEntry = true;
			}

			if (TEX->IsChunk("TILE")) { //Load GTA2 tiles
				if (newEntry) { //Do we need to load textures
					entry->TextureAtlas->CreateTextures(1024); //No texture data
					globalIDCounter += 1024;
					logWritem("Loading 1024 tiles (GTA2-exported tileset)");
					for (int i = 0; i < 1024; i++) {
						char newTexName[256];
						snprintf(newTexName,256,"%s_%d",graphicsName,i);
						entry->TextureAtlas->Textures[i]->textureName = mem.alloc_string(strlen(newTexName)+1);//(char*)mem.alloc(strlen(newTexName));
						strcpy(entry->TextureAtlas->Textures[i]->textureName,newTexName);

						float eps = 0.5f / 2048.0f;
						entry->TextureAtlas->Textures[i]->Left		= 64*(i%32);
						entry->TextureAtlas->Textures[i]->Width		= 64;
						entry->TextureAtlas->Textures[i]->Top		= 64*(i/32);
						entry->TextureAtlas->Textures[i]->Height	= 64;
						entry->TextureAtlas->Textures[i]->U1		= (64*(i%32)) / 2048.0f + eps;
						entry->TextureAtlas->Textures[i]->V1		= (64*(i/32)) / 2048.0f + eps;
						entry->TextureAtlas->Textures[i]->U2		= (63+64*(i%32)) / 2048.0f - eps;
						entry->TextureAtlas->Textures[i]->V2		= (63+64*(i/32)) / 2048.0f - eps;
					}
				}

				if (!scanOnly) {
					entry->TextureAtlas->CreateData(2048,2048);
					int readPos = 0;
					int prevPos = 0;
					while (TEX->chunkSize > 0) {
						int bytes = TEX->Read((char*)entry->TextureAtlas->textureData+readPos,min(2048,TEX->chunkSize - TEX->chunkPos));
						if (bytes == 0) break;
						readPos += bytes;
					}
					entry->TextureAtlas->dataValid = true;
				}
			}
			if (TEX->IsChunk("SPR0")) { //Load GTA2 sprites (compatible format)
				int spriteCount;
				TEX->Read(&spriteCount,4);
				if (newEntry) {
					entry->TextureAtlas->CreateTextures(spriteCount);
					globalIDCounter += spriteCount;
				}
				logWritem("Loading %d sprites (GTA2-exported spriteset)",spriteCount);
				for (int i = 0; i < spriteCount; i++) {
					char newTexName[256];
					int L,R,T,B;
									
					TEX->ReadString(newTexName);
					TEX->Read(&L,4);
					TEX->Read(&R,4);
					TEX->Read(&T,4);
					TEX->Read(&B,4);
	
					if (newEntry) {
						entry->TextureAtlas->Textures[i]->textureName = mem.alloc_string(strlen(newTexName)+1);//(char*)mem.alloc(strlen(newTexName));
						strcpy(entry->TextureAtlas->Textures[i]->textureName,newTexName);

						entry->TextureAtlas->Textures[i]->Left		= L;
						entry->TextureAtlas->Textures[i]->Width		= R-L;
						entry->TextureAtlas->Textures[i]->Top		= T;
						entry->TextureAtlas->Textures[i]->Height	= B-T;
						entry->TextureAtlas->Textures[i]->U1		= L / 2048.0f;
						entry->TextureAtlas->Textures[i]->V1		= T / 2048.0f;
						entry->TextureAtlas->Textures[i]->U2		= R / 2048.0f;
						entry->TextureAtlas->Textures[i]->V2		= B / 2048.0f;
					}
				}

				if (!scanOnly) {
					entry->TextureAtlas->CreateData(2048,2048);
					int readPos = 0;
					while (TEX->chunkSize > 0) {
						int bytes = TEX->Read((char*)entry->TextureAtlas->textureData+readPos,min(2048,TEX->chunkSize - TEX->chunkPos));
						if (bytes == 0) break;
						readPos += bytes;
					}
					while (readPos < 2048*2048*4) {
						*((char*)entry->TextureAtlas->textureData+readPos) = 0;
						readPos++;
					}
					entry->TextureAtlas->dataValid = true;
				}
			}
	}
}
示例#14
0
void Graphics_Manager::Initialize(bool PreloadTextures) {
	//Update maps info
	logWrite("Initializing graphics manager (updating texture file entries)");

	int TexSize = 0;
	DIR *directory;
	struct dirent *direntry;

	globalIDCounter = 0;
	graphicsEntries.Create();
	graphicsEntries.Preallocate(MAX_TEXTURE_FILE_ENTRIES);
	Animations.Initialize();

	directory = opendir("./data");
	while ((directory) && ((direntry = readdir(directory)) != 0)) {
		int len = strlen(direntry->d_name);
		switch (len) {
		case 1:
			if (direntry->d_name[0] == '.') continue;
		case 2:
			if (direntry->d_name[0] == '.' && direntry->d_name[1] == '.') continue;
		default:
			break;
		}

		//Check file format
		Chunk_Loader TEX;
		//Read map header
		char Filename[256];
		snprintf(Filename,256,"./data/%s",direntry->d_name);

		TEX.Open(Filename);
		if (TEX.IsFileFormat("ZTEX",TEX_VERSION) == false) {
			continue;
		}

		logWrite("Loading graphics from %s...",Filename);

		//Read all chunks from file
		int lastRead = 0;
		while (TEX.ReadChunk()) {
			LockID lockID = Thread.EnterLock(MUTEX_CRESOURCE_LOAD);
				Graphics.texReadGTA2Graphics(&TEX, false);
				Graphics.texReadFonts(&TEX, false);
				Animations.LoadFromChunk(&TEX);				
			Thread.LeaveLock(lockID);
		}
		TexSize += TEX.fileSize;
		TEX.Close();
	}
	if (directory) closedir(directory);

	totalTextures = globalIDCounter + 1;

	logWrite("Found %d KB worth of texture files (%d textures)",TexSize/1024,totalTextures);

	logWritem("shrinking graphicsEntries, animationEntries, fontEntries...");
	LockID lockID = Thread.EnterLock(MUTEX_CRESOURCE_LOAD);
		graphicsEntries.Shrink();
	Thread.LeaveLock(lockID);
	Animations.Entries.Shrink();
	//FIXME: should be shrink call directly
	Fonts.shrinkFontEntries(); //FIXME: ??? this should shrink font entries, call only after all fonts have been loaded
}
示例#15
0
void Map_Manager::Initialize() {
	logWrite("Initializing map manager (scanning for maps)");

	DIR *directory;
	struct dirent *direntry;

	int MapSize = 0; //Total size of maps loaded (for stats)

	mapsInfo.Preallocate(1024);
	cacheCityscape.Preallocate(1024);

	directory = opendir("./data");
	while ((directory) && ((direntry = readdir(directory)) != 0)) {
		int len = strlen(direntry->d_name);
		switch (len) {
			case 1:
				if (direntry->d_name[0] == '.') continue;
			case 2:
				if (direntry->d_name[0] == '.' && direntry->d_name[1] == '.') continue;
			default:
				break;
		}

		Chunk_Loader RMP;

		//Read the map
		char Filename[256];
		snprintf(Filename,256,"./data/%s",direntry->d_name);

		RMP.Open(Filename);
		if (RMP.IsFileFormat("ZRMP",RMP_VERSION) == false) {
			continue;
		}

		//Read all chunks from file
		while (RMP.ReadChunk()) {
			if (RMP.IsChunk("RMAP")) { //Scan the chunk for district name, and cell coordinates
				int CX,CY;
				char districtName[5] = "    ";
				RMP.Read(&CX,4);
				RMP.Read(&CY,4);
				RMP.Read(districtName,4);
				
				//Check if the map entry was already there
				int idx = cellEntryIndex(CX,CY);
				if (idx == BAD_ID) {
					logWritem("Found cell [%d;%d belongs to %s]: %s",CX,CY,districtName,direntry->d_name);

					rmp_entry* entry = mapsInfo.Add();
					entry->CX = CX;
					entry->CY = CY;
					strncpy(entry->Filename,Filename,256);
				} else {
					logWritem("Duplicate map cell! No place for [%d;%d] from %s",CX,CY,direntry->d_name);
				}
			}
		}
		MapSize += RMP.fileSize;
		RMP.Close();
	}
	if (directory) closedir(directory);
	logWrite("Found %d KB worth of maps",MapSize/1024);
	
	//Shrink entry table
	mapsInfo.Shrink();
}