summaryrefslogblamecommitdiff
path: root/testxml/testxml.py
blob: 10a4d60c9837cd77366e0e5cda03a067d2eddc88 (plain) (tree)
1
2
3
4
5
6
7
8
9


                         
            
             
           

         
               
          
                 

                           
           
 
                                                  
                                                




                                
                                    
               
                 


               
               
               
 





              















                                                                                                 
                                  
                               

                                        


































                                                          
                                               














                                                                                                
                                           
                                                              
                                                                                        
























                                                                                                                   
                       






                                                                           

                                                                       

                                                                             

                                                                        
















                                                    
                                         
                          
                                                                     



                                       
                                                                             



                                 
                                                                                



                                                                                 
                                                                                                    
                                        
                                                                           


                                
                                               
                         
                                                                        




                                          
                                                                                



                                                                                     
                                                                                        



                                            
                                               
                          
                                                                     
                         


                                       
                                                                             
                                
                                                                       
                                
















                                                                                      

 
                                              


                                                           
                                                                                  








                                                             
                                                                     



                                             
                                                                              
                                                      
                                                                                 

                                                            




                                                                                      
 
                                                                       
                                                                                            
 





                                                                                     




                                                                






                                                                                          
                                                                                                        


                                                                  
                                                                                                          


                                                                                  
                                                                                                                   

                                                             




                                                                                         

                                                 
                                                                                   
                                                   
                                                                                      

 
                                         



                              
                                                                

                        




                                                                             
                                                                                            
 
                                                                      

                      







                                             









                                                                       

                                                        
                                                   























                                                                                    











                                                                                                                 





                                                                               

                                                    
                                                                         


                                                                                                             
                                                                          


                                                                                                              




                                                                                               


                                                                                


















                                                                                               
                                






                                                                      
                                                                                                 





                                                                                          








                                                                                                          


                                                           

                        


















                                                                                            








                                                                                     
                                    






                                                                                        
 



                                                                                  

                                                                                                       





















                                                                                                          


                                                                                    
                                       



























                                                                                                                












                                                                                               
 












                                                         
                                                                                 
                               







                                                                                                                                                          
 
                       


















                                                                                    





                                                                                             

                        



                                                                             
                      




                                                                                

                                


                                                                             
                      


































                                                                                        
 

                                
                                        




























                                                                    
                    










                                                              









                                                                           







                                                                                 
                                      
                                                         
                                       


















                                                                                              
                                                                                             
 


                                                                                             



                                                                                       
                                                                      

                                                    
 
                            
                                                                                   
                               






                                                                                                       
 






                                                                                             








                                                                                                     




                                                                                          
                                                                





                                                                              

                               
                                     





















                                                            
                                                    
                                                  
                                               
                                                               









                                                                                                    
                                           






                                                                                 
                                                 
 
 










                                                                      


                                                                                                     


                                                                                                   
                                                   
 
                                                   
 


















                                                             
                                                           
 

                                                        
 
























                                                                                                     



                                                                                  
 

                         





                                                                                                     








                                                                                      



                                                     






                                        

















                                                                                                                




                                                           


                                                       
                                                                                                       
                                                                                                                


                                                                   


                                                                                   
























                                                                                                                   
                                                                        



                                                                                                                   

                                              





                                                               


                        

                          

                           



                                                                                  

                               


                                                                                 
                                    



                                                                                    
                                       
                                          
                                          
                     
                                           
                        






                                                                                                 


                                   




                                                                                                      
                                                                           
 
                                                                                




                                                                  




                                                                                            



                                                               
 


                                     
 














                                                                                 












                                    
                        

                              













                                                                               
                            




                      

                                              
               

                                                  



                                           




                                                         
                                                                            


                                                             



                                                                             
                        










                                                                          






                                                                                                       

                                                                 



                                                                            











































                                                                                                                                 













                                                                       
 
 




















                                                                         
 
            

                                        
                                
                   
           


                                 
                 
            
#! /usr/bin/env python2.6
# -*- coding: utf8 -*-

import array
import base64
import gzip
import os
import re
import datetime
import xml
import ogg.vorbis
from xml.dom import minidom
from PIL import Image
import zlib

filt = re.compile(".+[.](xml|tmx)", re.IGNORECASE)
filtmaps = re.compile(".+[.]tmx", re.IGNORECASE)
dyesplit1 = re.compile(";")
dyesplit2 = re.compile(",")
parentDir = "../../clientdata"
iconsDir = "graphics/items/"
spritesDir = "graphics/sprites/"
particlesDir = "graphics/particles/"
sfxDir = "sfx/"
mapsDir = "maps/"
errors = 0
warnings = 0
errDict = set()
safeDye = False
borderSize = 20

class Tileset:
	None

class Layer:
	None

def printErr(err):
	errDict.add(err)
	print err

def showFileErrorById(id, rootDir, fileDir):
	print "error: id=" + id + ", file not found: " + fileDir + " (" + rootDir + fileDir + ")"

def showFileWarningById(id, rootDir, fileDir):
	print "warn: id=" + id + ", file not found: " + fileDir + " (" + rootDir + fileDir + ")"

def showError(id, text):
	print "error: id=" + id + " " + text

def showWarning(id, text):
	print "warn: id=" + id + " " + text

def showMsg(id, text, src, iserr):
	global errors, warnings
	if text != "":
		text = text + ", " + src
	if iserr == True:
		if text not in errDict:
			showError(id, text)
			errDict.add(text)
			errors = errors + 1
	else:
		if text not in errDict:
			showWarning(id, text)
			errDict.add(text)
			warnings = warnings + 1

def showMsgSprite(file, text, iserr):
	global errors, warnings
	if iserr == True:
		err = "error: sprite=" + file + " " + text
		if err not in errDict:
			printErr(err)
			errors = errors + 1
	else:
		err = "warn: sprite=" + file + " " + text
		if err not in errDict:
			printErr(err)
			warnings = warnings + 1

def showMsgFile(file, text, iserr):
	global errors, warnings
	if iserr == True:
		err = "error: file=" + file + " " + text
		if err not in errDict:
			printErr(err)
			errors = errors + 1
	else:
		err = "warn: file=" + file + " " + text
		if err not in errDict:
			printErr(err)
			warnings = warnings + 1
	

def showFileMsgById(id, rootDir, fileDir, iserr):
	global errors, warnings
	if iserr == True:
		showFileErrorById(id, rootDir, fileDir)
		errors = errors + 1
	else:
		showFileWarningById(id, rootDir, fileDir)
		warnings = warnings + 1

def printSeparator():
	print "--------------------------------------------------------------------------------"

def showHeader():
	print "Evol client data validator."
	print "Run at: " + datetime.datetime.now().isoformat()
	print "http://www.gitorious.org/evol/evol-tools/blobs/master/testxml/testxml.py"
	printSeparator()

def showFooter():
	printSeparator()
	print "Total:"
	print " Warnings: " + str(warnings)
	print " Errors:   " + str(errors)

def enumDirs(parentDir):
	global warnings, errors
	files = os.listdir(parentDir) 
	for file1 in files:
		if file1[0] == ".":
			continue
		file2 = os.path.abspath(parentDir + os.path.sep + file1)
		if not os.path.isfile(file2):
			enumDirs(file2)
		elif filt.search(file1):
			try:
				minidom.parse(file2)
			except xml.parsers.expat.ExpatError as err:
				print "error: " + file2 + ", line=" + str(err.lineno) + ", char=" + str(err.offset)
				errors = errors + 1

def loadPaths():
	global warnings
	try:
		dom = minidom.parse(parentDir + "/paths.xml")
		for node in dom.getElementsByTagName("option"):
			if node.attributes["name"].value == "itemIcons":
				iconsDir = node.attributes["value"].value
			elif node.attributes["name"].value == "sprites":
				spritesDir = node.attributes["value"].value
			elif node.attributes["name"].value == "sfx":
				sfxDir = node.attributes["value"].value
			elif node.attributes["name"].value == "particles":
				particlesDir = node.attributes["value"].value
			elif node.attributes["name"].value == "maps":
				mapsDir = node.attributes["value"].value
	except:
		print "warn: paths.xml not found"
		warnings = warnings + 1

def splitImage(image):
	try:
		idx = image.find("|")
		if idx > 0:
			imagecolor = image[idx + 1:]
			image = image[0:idx]
		else:
			imagecolor = ""
	except:
		image = ""
		imagecolor = ""
	return [image, imagecolor]

def testDye(id, color, text, src, iserr):
	if len(color) < 4:
		showMsg(id, "dye to small size: " + text, src, iserr)
		return
	colors = dyesplit1.split(color)
	for col in colors:
		if len(col) < 4:
			showMsg(id, "dye to small size: " + text, src, iserr)
			continue

		c = col[0];
		if col[1] != ":":
			showMsg(id, "incorrect dye string: " + text, src, iserr)
			continue

		if c != "R" and c != "G" and c != "B" and c != "Y" and c != "M" \
			and c != "C" and c != "W":
				showMsg(id, "incorrect dye color: " + c + " in " + text, src, iserr)
				continue
		if testDyeInternal(id, col[2:], text, src, iserr) == False:
			continue


def testDyeInternal(id, col, text, src, iserr):
	if col[0] != "#":
		showMsg(id, "incorrect dye colors: " + text, src, iserr)
		return False

	paletes = dyesplit2.split(col[1:])
	for palete in paletes:
		if len(palete) != 6:
			showMsg(id, "incorrect dye palete: " + text, src, iserr)
			return False
		
		for char in palete.lower():
			if (char < '0' or char > '9') and (char < 'a' or char > 'f'):
				showMsg(id, "incorrect dye palete: " + text, src, iserr)
				return False
	return True


def testDyeColors(id, color, text, src, iserr):
	if len(color) < 4:
		showMsg(id, "dye to small size: " + text, src, iserr)
		return -1
	colors = dyesplit1.split(color)
	for col in colors:
		if len(col) < 4:
			showMsg(id, "dye to small size: " + text, src, iserr)
			continue
		if testDyeInternal(id, col, text, src, iserr) == False:
			continue
	return len(colors)

def testDyeMark(file, color, text, iserr):
	if len(color) < 1:
		showMsgSprite(file, "dye mark size to small:" + text, iserr)
		return -1
	colors = dyesplit1.split(color)
	for c in colors:
		if len(c) != 1:
			showMsgSprite(file, "dye mark incorrect size: " + text, iserr)
			continue
		
		if c != "R" and c != "G" and c != "B" and c != "Y" and c != "M" \
			and c != "C" and c != "W":
			showMsgSprite(file, "dye make incorrect: " + text, iserr)
 			continue
	return len(colors)


def testSprites(id, node, checkGender, iserr):
	try:
		tmp = node.getElementsByTagName("nosprite")
		if tmp is not None and len(tmp) > 1:
			showMsg(id, "more than one nosprite tag found", "", iserr)
		nosprite = True
	except:
		nosprite = False

	try:
		sprites = node.getElementsByTagName("sprite")
	except:
		sprites = None
		if nosprite == False:
			showMsg(id, "no sprite tag found", "", iserr)

	if sprites is not None:
		if len(sprites) == 0:
			if nosprite == False:
				showMsg(id, "no sprite tags found", "", iserr)
		elif len(sprites) > 3 and checkGender:
			showMsg(id, "incorrect number of sprite tags", "", iserr)
		elif len(sprites) == 1:
			file = sprites[0].childNodes[0].data
			if checkGender:
				try:
					gender = sprites[0].attributes["gender"].value
				except:
					gender = ""

				if gender != "" and gender != "unisex":
					showMsg(id, "gender tag in alone sprite", "", iserr)

			try:
				variant = int(sprites[0].attributes["variant"].value)
			except:
				variant = 0

			testSprite(id, file, variant, iserr)
		else:
			male = False
			female = False
			for sprite in sprites:
				file = sprite.childNodes[0].data
				if checkGender:
					try:
						gender = sprite.attributes["gender"].value
					except:
						gender = ""
					if gender == "male":
						if male == True:
							showMsg(id, "double male sprite tag", "", iserr)
						male = True
					elif gender == "female":
						if female == True:
							showMsg(id, "double female sprite tag", "", iserr)
						female = True
					elif gender == "unisex":
						if female == True or male == True:
							showMsg(id, "gender sprite tag with unisex tag", "", False)
						male = True
						female = True
				try:
					variant = int(sprite.attributes["variant"].value)
				except:
					variant = 0
				testSprite(id, file, variant, iserr)
			if checkGender:
				if male == False:
					showMsg(id, "no male sprite tag", "",iserr)
				if female == False:
					showMsg(id, "no female sprite tag", "", iserr)


def testSprite(id, file, variant, iserr):
	tmp = splitImage(file)
	color = tmp[1]
	file2 = tmp[0]
	if color != "":
		dnum = testDyeColors(id, color, file, "", iserr)
	else:
		dnum = 0

	fullPath = os.path.abspath(parentDir + "/" + spritesDir + file2)
	if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
		showFileMsgById(id, spritesDir, file2, iserr)
	else:
		testSpriteFile(id, fullPath, file, spritesDir + file2, dnum, variant, iserr)

def testSpriteFile(id, fullPath, file, fileLoc, dnum, variant, iserr):
	global safeDye
	
	try:
		dom = minidom.parse(fullPath)
	except:
		return

	if len(dom.childNodes) < 1:
		return

	try:
		variants = dom.attributes["variants"].value
	except:
		variants = 0

	try:
		variant_offset = dom.attributes["variant_offset"].value
	except:
		variant_offset = 0
		
	root = dom.childNodes[0];
	imagesets = dom.getElementsByTagName("imageset")
	if imagesets is None or len(imagesets) < 1:
		showMsgSprite(fileLoc, "incorrect number of imageset tags", iserr)
	imageset = imagesets[0]
	
	try:
		image = imageset.attributes["src"].value
		image0 = image
		img = splitImage(image)
		image = img[0]
		imagecolor = img[1]
	except:
		showMsgSprite(fileLoc, "image attribute not exist: " + image, iserr)
		return

	try:
		width = imageset.attributes["width"].value
	except:
		showMsgSprite(fileLoc, "no width attribute", iserr)
		return

	try:
		height = imageset.attributes["height"].value
	except:
		showMsgSprite(fileLoc, "no height attribute", iserr)
	
	if imagecolor != "":
		num = testDyeMark(fileLoc, imagecolor, image0, iserr)
		if safeDye == False and dnum != num:
			if dnum > num:
				e = iserr
			else:
				e = False
			showMsgSprite(fileLoc, "dye colors size not same in sprite (" + str(num) \
			+ ") and in caller (" + str(dnum) + ", id=" + str(id) + ")", e)
	elif safeDye == True and dnum > 0:
			showMsgSprite(fileLoc, "dye set in sprite but not in caller (id=" + str(id) + ")", False)


	fullPath = os.path.abspath(parentDir + "/" + image)
	if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
		showMsgSprite(fileLoc, "image file not exist: " + image, iserr)
		return
	sizes = testImageFile(image, fullPath, 0, iserr)
	s1 = int(sizes[0] / int(width)) * int(width)
	if sizes[0] != s1:
		showMsgSprite(fileLoc, "image width " + str(sizes[0]) + \
		" (need " + str(s1) + ") is not multiply to frame size " + width + ", image:" + image, False)
	s2 = int(sizes[1] / int(height)) * int(height)
	if sizes[1] != s2:
		showMsgSprite(fileLoc, "image height " + str(sizes[1]) + \
		" (need " + str(s2) + ") is not multiply to frame size " + height + ", image:" + image, False)

	num = (s1 / int(width)) * (s2 / int(height))
	if variants == 0 and variant > 0:
		showMsgSprite(fileLoc, "missing variants attribute in sprite", iserr)
	if variants > 0 and variant >= variants:
		showMsgSprite(fileLoc, "variant number more then in variants attribute", iserr)

	if variant >= num:
		showMsgSprite(fileLoc, "to big variant number " + str(variant) \
		+ ". Frames number " + str(num) + ", id=" + str(id), iserr)
	if num < 1:
		showMsgSprite(fileLoc, "image have zero frames: " + iamge, iserr)

	try:
		includes = dom.getElementsByTagName("include")
	except:
		includes = None

	#todo need parse included files

	try:
		actions = dom.getElementsByTagName("action")
	except:
		actions = None

	if (actions == None or len(actions) == 0) and (includes == None or len(includes) == 0):
		showMsgSprite(fileLoc, "no actions in sprite file", iserr)
	else:
		actset = set()
		frameSet = set()
		for action in actions:
			try:
				name = action.attributes["name"].value
			except:
				showMsgSprite("no action name", iserr)
				continue

			frameSet = frameSet | testSpriteAction(fileLoc, name, action, num, iserr)

			if name in actset:
				showMsgSprite(fileLoc, "duplicate action: " + name, iserr)
				continue
			actset.add(name)

		if len(frameSet) > 0:
			errIds = ""
			i = 0
			while i < max(frameSet):
				if i not in frameSet:
					errIds = errIds + str(i) + ","
				i = i + 1
			if len(errIds) > 0:
				showMsgSprite(fileLoc, "unused frames: " + errIds[0:len(errIds)-1], False)


def testSpriteAction(file, name, action, numframes, iserr):
	framesid = set()
	
	try:
		animations = action.getElementsByTagName("animation")
	except:
		animations = None
	
	if animations == None or len(animations) == 0:
		showMsgSprite(file, "no animation tags in action: " + name, False)

	aniset = set()
	for animation in animations:
		try:
			direction = animation.attributes["direction"].value
		except:
			direction = "default"

		if direction is aniset:
			showMsgSprite(file, "duplicate direction in action: " + name, iserr)
			continue
		aniset.add(direction)

		lastIndex1 = -1
		lastIndex2 = -1
		lastOffsetX = 0
		lastOffsetY = 0
		cnt = 0

		for node2 in animation.childNodes:
			if node2.nodeName == "frame" or node2.nodeName == "sequence":
				try:
					offsetX = int(node2.attributes["offsetX"].value)
				except:
					offsetX = 0
				try:
					offsetY = int(node2.attributes["offsetY"].value)
				except:
					offsetY = 0

			if node2.nodeName == "frame":
				frame = node2	
				try:
					idx = int(frame.attributes["index"].value)
				except:
					showMsgSprite(file, "no frame index in action: " + name, iserr)
					
				if idx >= numframes or idx < 0:
					showMsgSprite(file, "incorrect frame index " + str(idx) + \
							" aciton: " + name + ", direction: "\
							+ direction, iserr)
				else:
					framesid.add(idx)
				if lastIndex1 == idx and lastIndex2 == -1 and offsetX == lastOffsetX \
				and offsetY == lastOffsetY:
					showMsgSprite(file, "duplicate frame animation for frame index=" \
							+ str(idx) + " action: " + name + \
							", direction: " + direction, False)
				else:
					lastIndex1 = idx
					lastIndex2 = -1
					lastOffsetX = offsetX
					lastOffsetY = offsetY

				framesid.add(idx)
				cnt = cnt + 1
			elif node2.nodeName == "sequence":
				sequence = node2
				try:
					i1 = int(sequence.attributes["start"].value)
					i2 = int(sequence.attributes["end"].value)
				except:
					showMsgSprite(file, "no sequence start or end index action: " + \
							name + ", direction: " + direction, iserr)
					
				if i1 >= numframes or i1 < 0:
					showMsgSprite(file, "incorrect start sequence index " + str(i1) + \
							" action: " + name + ", direction: " + direction, iserr)
				if i2 >= numframes or i2 < 0:
					showMsgSprite(file, "incorrect end sequence index " + str(i2) + \
							" action: " + name + ", direction: " + direction, iserr)
				if i1 == i2:
					showMsgSprite(file, "start and end sequence index is same. " \
							+ "May be better use frame? action: " + \
							name + ", direction: " + direction, False)

				if lastIndex1 == i1 and lastIndex2 == i2 and offsetX == lastOffsetX \
				and offsetY == lastOffsetY:
					showMsgSprite(file, "duplicate sequence animation for start=" \
							+ str(i1) + ", end=" + str(i2) + " action: " + \
							name + ", direction: " + direction, False)
				else:
					lastIndex1 = i1
					lastIndex2 = i2
					lastOffsetX = offsetX
					lastOffsetY = offsetY
				
				cnt = cnt + 1
				for i in range(i1,i2 + 1):
					framesid.add(i)

		if cnt == 0:
			showMsgSprite(file, "no frames or sequences in action: " + name, iserr)

	if "default" not in aniset:
		if "down" not in aniset:
			showMsgSprite(file, "no down direction in animation: " + name, iserr)
		if "up" not in aniset:
			showMsgSprite(file, "no up direction in animation: " + name, iserr)
		if "left" not in aniset:
			showMsgSprite(file, "no left direction in animation: " + name, iserr)
		if "right" not in aniset:
			showMsgSprite(file, "no right direction in animation: " + name, iserr)

	if name == "dead" and len(animations) > 0:
		lastani = animations[len(animations) - 1]
		lastNode = None
		nc = 0
		for node in lastani.childNodes:
			if node.nodeName == "frame":
				lastNode = node
				nc = nc + 1
			if node.nodeName == "sequence":
				lastNode = node
				nc = nc + 2
		if nc > 1:
			try:
				cont = int(lastNode.attributes["continue"].value)
			except:
				cont  = 0;
			if cont == 0:
				try:
					delay = int(lastNode.attributes["delay"].value)
				except:
					delay = 0
				if delay > 0 and delay < 5000:
					showMsgSprite(file, "last frame\sequence in dead animation have to low limit. Need zero or >5000: " + name, False)

	return framesid


def testImageFile(file, fullPath, sz, iserr):
	try:
		img = Image.open(fullPath, "r")
		img.load()
	except:
		showMsgFile(file, "incorrect image format", iserr)
		return

	if img.format != "PNG":
		showMsgFile(file, "image format is not png", False)

	sizes = img.size
	if sz != 0:
		if sizes[0] > sz or sizes[1] > sz:
			showMsgFile(file, "image size incorrect (" + str(sizes[0]) \
			+ "x" + str(sizes[1]) + ") should be (" + str(sz) + "x" \
			+ str(sz) + ")", iserr)
		elif sizes[0] < sz or sizes[1] < sz:
			showMsgFile(file, "possible image size incorrect (" + str(sizes[0]) \
			+ "x" + str(sizes[1]) + ") should be (" + str(sz) + "x" \
			+ str(sz) + ")", False)


	return sizes	

def testSound(file):
	fullPath = parentDir + "/" + sfxDir + file
	if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
		showMsgFile(file, "sound file not found", True)
		return
	try:
		snd = ogg.vorbis.VorbisFile(fullPath)
	except ogg.vorbis.VorbisError as e:
		showMsgFile(file, "sound file incorrect error: " + str(e), True)


def testParticle(id, file, src):
	fullPath = parentDir + "/" + file
	if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
		showMsgFile(file, "particle file not found", True)
		return
	dom = minidom.parse(fullPath)

	nodes = dom.getElementsByTagName("particle")
	if len(nodes) < 1:
		showMsgFile(file, "missing particle tags", False)
	else:
		for node in nodes:
			testEmitters(id, file, node, file)


def testEmitters(id, file, parentNode, src):
	for node in parentNode.getElementsByTagName("property"):
		try:
			name = node.attributes["name"].value
		except:
			showMsgFile(file, "missing attribute name in emitter" \
					" in particle file", True)
			continue
		try:
			value = node.attributes["value"].value
		except:
			value = None

		if name == "image":
			if value == None:
				showMsgFile(file, "missing attribute value in emitter" \
						" image attribute", True)
			img = splitImage(value)
			image = img[0]
			imagecolor = img[1]
			testDye(id, imagecolor, "image=" + image, src, True)
			testImageFile(image, parentDir + "/" + image, 0, True)
	for node in parentNode.getElementsByTagName("emitter"):
		testEmitters(id, file, node, src)



def testItems(fileName, imgDir):
	global warnings, errors, safeDye
	print "Checking items.xml"
	dom = minidom.parse(parentDir + fileName)
	idset = set()
	for node in dom.getElementsByTagName("item"):
		id = node.attributes["id"].value
		if id in idset:
			print "error: duplicated id=" + id
			errors = errors + 1
		else:
			idset.add(id)
			
		idI = int(id)
		try:
			type = node.attributes["type"].value
		except:
			type = ""
			print "warn: no type attribute for id=" + id
			warnings = warnings + 1
		try:
			image = node.attributes["image"].value
			image0 = image
			img = splitImage(image)
			image = img[0]
			imagecolor = img[1]
		except:
			image = ""
			image0 = ""
			imagecolor = ""

		try:
			floor = node.attributes["floor"].value
			floor0 = floor
			flr = splitImage(floor)
			floor = flr[0]
			floorcolor = flr[1]
		except:
			floor = None
			floor0 = None
			floorcolor = None

		try:
			description = node.attributes["description"].value
		except:
			description = ""

		try:
			missile = node.attributes["missile-particle"].value
		except:
			missile = ""


		if type == "hairsprite":
			if idI >= 0:
				print "error: hairsprite with id=" + id
				errors = errors + 1
			elif idI < -100:
				print "error: hairsprite override player sprites"
				errors = errors + 1

			safeDye = True
			testSprites(id, node, True, True)
			safeDye = False

		elif type == "racesprite":
			if idI >= 0:
				print "error: racesprite with id=" + id
				errors = errors + 1
			elif idI > -100:
				print "error: racesprite override player hair"
				errors = errors + 1
		elif type == "usable" or type == "unusable" or type == "generic" \
		or type == "equip-necklace" or type == "equip-torso" or type == "equip-feet" \
		or type == "equip-arms" or type == "equip-legs" or type == "equip-head" \
		or type == "equip-shield" or type == "equip-1hand" or type == "equip-2hand" \
		or type == "equip-charm" or type == "equip-ammo" or type == "equip-neck" \
		or type == "equip-ring":
			if image == "":
				print "error: missing image attribute on id=" + id
				errors = errors + 1
				continue
			elif len(imagecolor) > 0:
				testDye(id, imagecolor, "image=" + image0, "items.xml", True)

			if floorcolor != None and len(floorcolor) > 0:
				testDye(id, floorcolor, "floor=" + floor0, "items.xml", True)

			if description == "":
				print "warn: missing description attribute on id=" + id
				warnings = warnings + 1
			if missile != "":
				testParticle(id, missile, "items.xml")

			testSounds(id, node, "item")

			try:
				floorSprite = node.getElementsByTagName("floor")[0]
			except:
				floorSprite = None
			if floorSprite != None:
				if floor != None:
					print "error: found attribute floor and tag floor. " + \
							"Should be only one tag or attribute. id=" + id
					errors = errors + 1
				testSprites(id, floorSprite, False, err)

			fullPath = os.path.abspath(parentDir + "/" + imgDir + image)
			if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
				showFileErrorById (id, imgDir, image)
				errors = errors + 1
			else:
				testImageFile(imgDir + image, fullPath, 32, True)

			if floor != None:
				fullPath = os.path.abspath(parentDir + "/" + imgDir + floor)
				if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
					showFileErrorById (id, imgDir, floor)
					error = errors + 1
				else:
					testImageFile(imgDir + floor, fullPath, 0, True)


			if type != "usable" and type != "unusable" and type != "generic" \
			and type != "equip-necklace" and type != "equip-1hand" \
			and type != "equip-2hand" and type != "equip-ammo" \
			and type != "equip-charm" and type != "equip-neck":
				err = type != "equip-shield"
				testSprites(id, node, True, err)
		elif type == "other":
			None
		elif type != "":
			print "warn: unknown type '" + type + "' for id=" + id
			warnings = warnings + 1

def testMonsters(fileName):
	global warnings, errors
	print "Checking monsters.xml"
	dom = minidom.parse(parentDir + fileName)
	idset = set()
	for node in dom.getElementsByTagName("monster"):
		try:
			id = node.attributes["id"].value
		except:
			print "error: no id for monster"
			errors = errors + 1
			continue

		if id in idset:
			print "duplicate id=" + id
		else:
			idset.add(id)

		try:
			name = node.attributes["name"].value
		except:
			print "error: no name for id=" + id
			errors = errors + 1
			name = ""

		testTargetCursor(id, node, fileName)
		testSprites(id, node, False, True)
		testSounds(id, node, "monster")
		testParticles(id, node, "particlefx", fileName)


def testTargetCursor(id, node, file):
	try:
		targetCursor = node.attributes["targetCursor"].value
		if targetCursor != "small" and targetCursor != "medium" and targetCursor != "large":
			showMsgFile(id, "unknown target cursor " + targetCursor)
	except:
		None

def testParticles(id, node, nodeName, src):
	particles = node.getElementsByTagName(nodeName)
	for particle in particles:
		try:
			particlefx = particle.childNodes[0].data
		except:
			showMsgFile(id, "particle tag have incorrect data", True)

		testParticle(id, particlefx, src)



def testSounds(id, node, type):
	global errors
	for sound in node.getElementsByTagName("sound"):
		try:
			event = sound.attributes["event"].value
		except:
			print "error: no sound event name in id=" + id
			errors = errors + 1

		if type == "monster":
			if event != "hit" and event != "miss" and event != "hurt" and event != "die":
				print "error: incorrect sound event name " + event + " in id=" + id
				errors = errors + 1
		elif type == "item":
			if event != "hit" and event != "strike":
				print "error: incorrect sound event name " + event + " in id=" + id
				errors = errors + 1

		testSound(sound.childNodes[0].data)

def testNpcs(file):
	global warnings, errors
	print "Checking npcs.xml"
	dom = minidom.parse(parentDir + file)
	idset = set()
	for node in dom.getElementsByTagName("npc"):
		try:
			id = node.attributes["id"].value
		except:
			print "error: no id for npc"
			errors = errors + 1
			continue
		
		if id in idset:
			print "error: duplicate npc id=" + id
		else:
			idset.add(id)

		testSprites(id, node, False, True)
		testParticles(id, node, "particlefx", file)

def readAttrI(node, attr, dv, msg, iserr):
	return int(readAttr(node, attr, dv, msg, iserr))

def readAttr(node, attr, dv, msg, iserr):
	global warnings, errors
	try:
		return node.attributes[attr].value
	except:
		print msg
		if iserr:
			errors = errors + 1
		else:
			warnings = warnings + 1
		return dv


def testMap(file, path):
	global warnings, errors
	fullPath = parentDir + "/" + path
	dom = minidom.parse(fullPath)
	root = dom.documentElement
	mapWidth = readAttrI(root, "width", 0, "error: missing map width: " + file, True)
	mapHeight = readAttrI(root, "height", 0, "error: missing map height: " + file, True)
	mapTileWidth = readAttrI(root, "tilewidth", 0, "error: missing tile width: " + file, True)
	mapTileHeight = readAttrI(root, "tileheight", 0, "error: missing tile height: " + file, True)
	if mapWidth == 0 or mapHeight == 0 or mapTileWidth == 0 or mapTileHeight == 0:
		return

	if mapWidth < borderSize * 2 + 1:
		showMsgFile(file, "map width to small: " + str(mapWidth), False)
	if mapHeight < borderSize * 2 + 1:
		showMsgFile(file, "map height to small: " + str(mapHeight), False)

	tilesMap = dict()

	for tileset in dom.getElementsByTagName("tileset"):
		name = readAttr(tileset, "name", "", "warning: missing tile name: " + file, False)
		tileWidth = readAttrI(tileset, "tilewidth", mapTileWidth, \
				"error: missing tile width in tileset: " + name + ", " + file, True)
		tileHeight = readAttrI(tileset, "tileheight", mapTileHeight, \
				"error: missing tile height in tileset: " + name + ", " + file, True)
		try:
			firstGid = int(tileset.attributes["firstgid"].value)
		except:
			firstGid = 0

		if firstGid in tilesMap:
			showMsgFile(file, "tile with firstgid " + str(firstGid) + \
					" already exist: " + name + ", " + file, True)
			continue

		if tileWidth == 0 or tileHeight == 0:
			continue

		tile = Tileset()
		tile.name = name
		tile.width = tileWidth
		tile.height = tileHeight
		tile.firstGid = firstGid
		tile.lastGid = 0

#		if tileWidth != 32:
#			showMsgFile(file, "tile width " + str(tileWidth) + " != 32: " + name, False)
#		if tileHeight != 32:
#			showMsgFile(file, "tile height " + str(tileHeight) + " != 32: " + name, False)

		images = tileset.getElementsByTagName("image")
		if images == None or len(images) == 0:
			showMsgFile(file, "missing image tags in tile " + name, True)
			continue
		elif len(images) > 1:
			showMsgFile(file, "to many image tags in tile " + name, True)
			continue
		else:
			image = images[0]
			source = readAttr(image, "source", None, "error: missing source in image tag in tile " \
					+ name + ": " + file, True)
			if source != None:
				imagePath = os.path.abspath(parentDir + "/" + mapsDir + source)

				img = splitImage(imagePath)
				imagePath = img[0]
				imagecolor = img[1]

				tile.image = imagePath
				tile.color = imagecolor

				if not os.path.isfile(imagePath) or os.path.exists(imagePath) == False:
					showMsgFile(file, "image file not exist: " + mapsDir + source + ", " + \
							name, True)
					continue

				if imagecolor != "":
					testDye("", imagecolor, source, file, True)

				sz = testImageFile(file, imagePath, 0, True)
				width = sz[0]
				height = sz[1]

				if width == 0 or height == 0:
					continue
				
				if width < tileWidth:
					showMsgFile(file, "tile width more than image width in tile: " + \
							name, True)
					continue
				if height < tileHeight:
					showMsgFile(file, "tile height more than image height in tile: " + \
							name, True)
					continue

				s1 = int(width / int(tileWidth)) * int(tileWidth)

				if width != s1:
					showMsgFile(file, "image width " + str(width) + \
							" (need " + str(s1) + ") is not multiply to tile size " + \
							str(tileWidth) + ". " + source + ", " + name, False)

				s2 = int(height / int(tileHeight)) * int(tileHeight)

				tile.lastGid = tile.firstGid + (s1 * s2)
				if height != s2:
					showMsgFile(file, "image width " + str(height) + \
							" (need " + str(s2) + ") is not multiply to tile size " + \
							str(tileHeight) + ". " + source + ", " + name, False)

		tilesMap[tile.firstGid] = tile
					

	layers = dom.getElementsByTagName("layer")
	if layers == None or len(layers) == 0:
		showMsgFile(file, "map dont have layers", True)
		return

	fringe = None
	collision = None
	lowLayers = set() 
	overLayers = set()
	beforeFringe = True

	for layer in layers:
		name = readAttr(layer, "name", None, "layer dont have name", True)
		if name == None:
			continue
		obj = Layer()
		obj.name = name
		if name.lower() == "fringe":
			if fringe is not None:
				showMsgFile(file, "duplicate Fringe layer", True)
			fringe = obj
			beforeFringe = False
		elif name.lower() == "collision":
			if collision is not None:
				showMsgFile(file, "duplicate Collision layer", True)
			collision = obj
		elif beforeFringe == True:
			lowLayers.add(obj)
		else:
			overLayers.add(obj)
			
		width = readAttrI(layer, "width", 0, "error: missing layer width: " + name + \
				", " + file, True)
		height = readAttrI(layer, "height", 0, "error: missing layer height: " + name + \
				", " + file, True)
		if width == 0 or height == 0:
			continue

		obj.width = width
		obj.height = height

		if mapWidth < width:
			showMsgFile(file, "layer width " + str(width) + " more than map width " + \
					str(mapWidth) + ": " + name, True)
		if mapHeight < height:
			showMsgFile(file, "layer height " + str(height) + " more then map height " + \
					str(mapHeight) + ": " + name, True)

		obj = testLayer(file, layer, name, width, height, obj, tilesMap)

	if fringe == None:
		showMsgFile(file, "missing fringe layer", True)
	if collision == None:
		showMsgFile(file, "missing collision layer", True)
	else:
		ids =testCollisionLayer(file, collision)
		if ids != None and len(ids) > 0:
			showLayerErrors(file, ids, "empty tiles in collision border", False)

	if len(lowLayers) < 1:
		showMsgFile(file, "missing low layers", False)
	if len(overLayers) < 1:
		showMsgFile(file, "missing over layers", False)

	if fringe != None:
		lowLayers.add(fringe)
	warn1 = None

	if len(overLayers) > 0:
		warn1 = testLayerGroups(file, lowLayers, False)
		lowLayers = lowLayers | overLayers
		err1 = testLayerGroups(file, lowLayers, False)
	else:
		err1 = testLayerGroups(file, lowLayers, False)

	if warn1 != None and err1 != None:
		warn1 = warn1 - err1
	if warn1 != None and len(warn1) > 0:
		showLayerErrors(file, warn1, "empty tile in lower layers", False)
	if err1 != None and len(err1) > 0:
		showLayerErrors(file, err1, "empty tile in all layers", True)


def testCollisionLayer(file, layer):
	haveTiles = False
	tileset = set()
	arr = layer.arr
	x1 = borderSize 
	y1 = borderSize
	x2 = layer.width - 20
	y2 = layer.width - 20
	if x2 < 0:
		x2 = 0
	if y2 < 0:
		y2 = 0

	if arr is None :
		return tileset

	for x in range(0, layer.width):
		if haveTiles == True:
			break
		for y in range(0, layer.height):
			idx = ((y * layer.width) + x) * 4
			val = getLDV(arr, idx)
			if val != 0:
				haveTiles = True
			if val == 0 and (x < x1 or x > x2 or y < y1 or y > y2):
				tileset.add((x, y))

	
	if haveTiles == False:
		showMsgFile(file, "empty collision layer", False)
		return set()

	return tileset



def showLayerErrors(file, points, msg, iserr):
	txt = ""
	cnt = 0
	for point in points:
		txt = txt + " " + str(point) + ","
		cnt = cnt + 1 
		if cnt > 100:
			txt = txt + " ... "
			break
	showMsgFile(file, msg + txt[0:len(txt)-1], iserr)

	

def getLDV(arr, index):
	return arr[index] | (arr[index + 1] << 8) | (arr[index + 2] << 16) \
		    | (arr[index + 3] << 24)

def testLayer(file, node, name, width, height, layer, tiles):
	datas = node.getElementsByTagName("data")
	if datas == None or len(datas) == 0:
		showMsgFile(file, "missing data tag in layer: " + name, True)
		return
	layer.arr = None
	for data in datas:
		try:
			encoding = data.attributes["encoding"].value
		except:
			encoding = ""
		try:
			compression = data.attributes["compression"].value
		except:
			compression = ""
		if encoding == "base64":
			if compression != "gzip":
				if compression != "zlib":
					showMsgFile(file, "invalid compression " + compression + \
							" in layer: " + name, True)
					continue
				else:
					showMsgFile(file, "not supported compression by old clients " \
							+ compression + " in layer: " + name, False)
			binData = data.childNodes[0].data.strip()
			binData = binData.decode('base64')
			if compression == "gzip":
				dc = zlib.decompressobj(16 + zlib.MAX_WBITS)
			else:
				dc = zlib.decompressobj()
			layerData = dc.decompress(binData)
			arr = array.array("B")
			arr.fromstring(layerData)
			layer.arr = arr

			# here may be i should check is tiles correct or not, but i will trust to tiled
	return layer


def testLayerGroups(file, layers, iserr):
	width = 0
	height = 0
	errset = set()
	for layer in layers:
		if layer.width > width:
			width = layer.width
		if layer.height > height:
			height = layer.height

	for x in range(0, width):
		for y in range(0, height):
			good = False
			for layer in layers:
				if layer.arr != None and x < layer.width \
						and y < layer.height:
					arr = layer.arr
					ptr = ((y * layer.width) + x) * 4
					if arr[ptr] != 0 or arr[ptr + 1] != 0 or \
							arr[ptr + 2] != 0 or arr[ptr + 3] != 0:
								good = True
								break
			if good == False:
				errset.add((x,y))
#				showMsgFile(file, "empty tile in lower layers (" \
#						+ str(x) + "," + str(y) + ")", False)


					
#					for f in range(0, len(arr), 4):
#						flg = getLDV(arr, f)
#						y = int(f / 4 / layer.width)
#						x = int(f / 4) - (y * layer.width)
#						print "addr: " + str(f) + ", flg=" + str(flg) + ", x=" + str(x) + ", y=" + str(y)
	return errset


def testMaps(dir):
	global warnings, errors
	fullPath = parentDir + "/" + dir
	print "Checking maps"
	if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
		print "error: maps dir not found: " + dir
		errors = errors + 1
		return

	for file in os.listdir(fullPath):
		if filtmaps.search(file):
			testMap(mapsDir + file, dir + file)


def haveXml(dir):
	if not os.path.isdir(dir) or not os.path.exists(dir):
		return False
	for file in os.listdir(dir):
		if filt.search(file):
			return True
	return False
		

def detectClientData(dirs):
	global parentDir

	for dir in dirs:
		if haveXml(dir):
			print "Detected client data directory in: " + dir
			parentDir = dir
			return True
	
	print "Cant detect client data directory"
	exit(1)


showHeader()
print "Detecting clientdata dir"
detectClientData([".", "..", parentDir])
print "Checking xml file syntax"
enumDirs(parentDir)
loadPaths()
testItems("/items.xml", iconsDir)
testMonsters("/monsters.xml")
testNpcs("/npcs.xml")
testMaps(mapsDir)
showFooter()