summaryrefslogtreecommitdiff
path: root/testxml/testxml.py
diff options
context:
space:
mode:
Diffstat (limited to 'testxml/testxml.py')
-rwxr-xr-xtestxml/testxml.py2439
1 files changed, 2439 insertions, 0 deletions
diff --git a/testxml/testxml.py b/testxml/testxml.py
new file mode 100755
index 0000000..0b693cc
--- /dev/null
+++ b/testxml/testxml.py
@@ -0,0 +1,2439 @@
+#! /usr/bin/env python
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2010-2011 Evol Online
+# Author: Andrei Karas (4144)
+
+import array
+import os
+import re
+import datetime
+import xml
+import csv
+import ogg.vorbis
+import StringIO
+import sys
+from xml.dom import minidom
+from xml.etree import ElementTree
+from PIL import Image
+import zlib
+
+filt = re.compile(".+[.](xml|tmx|tsx)", re.IGNORECASE)
+filtmaps = re.compile(".+[.]tmx", re.IGNORECASE)
+filtimages = re.compile(".+[.]png", re.IGNORECASE)
+filtxmls = re.compile(".+[.]xml", re.IGNORECASE)
+filtogg = re.compile(".+[.]ogg", re.IGNORECASE)
+dyesplit1 = re.compile(";")
+dyesplit2 = re.compile(",")
+parentDir = "../../gittorious/clientdata-beta"
+iconsDir = "graphics/items/"
+spritesDir = "graphics/sprites/"
+particlesDir = "graphics/particles/"
+sfxDir = "sfx/"
+musicDir = "music/"
+mapsDir = "maps/"
+spriteErrorFile = "error.xml"
+levelUpEffectFile = "levelup.particle.xml"
+portalEffectFile = "warparea.particle.xml"
+minimapsDir = "graphics/minimaps/"
+wallpapersDir = "graphics/images/"
+wallpaperFile = "login_wallpaper.png"
+
+errors = 0
+warnings = 0
+errDict = set()
+safeDye = False
+borderSize = 14 # Required 18 # Original 14
+tiledVersion = 13 # Minimum Tiled version, advised "14" for Tiled 1.4
+colorsList = set()
+showAll = False
+silent = False
+stfu = False
+herc = False
+
+testBadCollisions = False
+# number of tiles difference. after this amount tiles can be counted as incorrect
+tileNumDiff = 3
+# max number of incorrect tiles. If more then tile not counted as error
+maxNumErrTiles = 5
+
+class Tileset:
+ None
+
+class Layer:
+ None
+
+def printErr(err):
+ errDict.add(err)
+ print err.encode("utf-8")
+
+def showFileErrorById(id, rootDir, fileDir):
+ rootDir = rootDir.encode("utf-8")
+ fileDir = fileDir.encode("utf-8")
+ print "error: id=" + id + ", file not found: " + fileDir + " (" + rootDir + fileDir + ")"
+
+def showFileWarningById(id, rootDir, fileDir):
+ rootDir = rootDir.encode("utf-8")
+ fileDir = fileDir.encode("utf-8")
+ print "warn: id=" + id + ", file not found: " + fileDir + " (" + rootDir + fileDir + ")"
+
+def showError(id, text):
+ text = text.encode("utf-8")
+ print "error: id=" + id + " " + text
+
+def showWarning(id, text):
+ text = text.encode("utf-8")
+ 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("Client Data Validator.")
+ print("Run at: " + datetime.datetime.now().isoformat())
+ print("https://git.themanaworld.org/ml/tools/blob/master/testxml/testxml.py")
+ printSeparator()
+
+def showFooter():
+ printSeparator()
+ print "Total:"
+ print " Warnings: " + str(warnings)
+ print " Errors: " + str(errors)
+
+def enumDirs(parentDir):
+ global warnings, errors
+ try:
+ files = os.listdir(parentDir)
+ except OSError:
+ print "Directory error: " + parentDir
+ if silent == False:
+ warnings = warnings + 1
+ return
+ 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)
+ else:
+ if filt.search(file1):
+ try:
+ if silent == True and not stfu:
+ print "Checking " + file2
+ minidom.parse(file2)
+ except xml.parsers.expat.ExpatError as err:
+ print "error: " + file2 + ", line=" + str(err.lineno) + ", char=" + str(err.offset)
+ errors = errors + 1
+ if file1 != "testxml.py":
+ checkFilePermission(file2)
+
+def checkFilePermission(fullName):
+ global warnings
+ if os.access(fullName, os.X_OK):
+ print "warn: execute flag on file: " + fullName
+ warnings = warnings + 1
+
+
+def loadPaths():
+ global warnings, iconsDir, spritesDir, sfxDir, particlesDir, mapsDir, spriteErrorFile, \
+ levelUpEffectFile, portalEffectFile, minimapsDir, wallpapersDir, walpaperFile, \
+ musicDir, wallpaperFile
+ try:
+ dom = minidom.parse(parentDir + "/paths.xml")
+ for node in dom.getElementsByTagName("option"):
+ if node.attributes["name"].value == "itemIcons":
+ iconsDir = node.attributes["value"].value
+ if iconsDir != "graphics/items/":
+ print "warn: itemIcons path has not default value."\
+ " Will be incampatible with old clients."
+ warnings = warnings + 1
+ elif node.attributes["name"].value == "sprites":
+ spritesDir = node.attributes["value"].value
+ if spritesDir != "graphics/sprites/":
+ print "warn: sprites path has not default value."\
+ " Will be incampatible with old clients."
+ warnings = warnings + 1
+ elif node.attributes["name"].value == "sfx":
+ sfxDir = node.attributes["value"].value
+
+ elif node.attributes["name"].value == "particles":
+ particlesDir = node.attributes["value"].value
+ if particlesDir != "graphics/particles/":
+ print "warn: particles path has not default value."\
+ " Will be incampatible with old clients."
+ warnings = warnings + 1
+ elif node.attributes["name"].value == "maps":
+ mapsDir = node.attributes["value"].value
+ if mapsDir != "maps/":
+ print "warn: maps path has not default value."\
+ " Will be incampatible with old clients."
+ warnings = warnings + 1
+ elif node.attributes["name"].value == "spriteErrorFile":
+ spriteErrorFile = node.attributes["value"].value
+ elif node.attributes["name"].value == "levelUpEffectFile":
+ levelUpEffectFile = node.attributes["value"].value
+ elif node.attributes["name"].value == "portalEffectFile":
+ portalEffectFile = node.attributes["value"].value
+ elif node.attributes["name"].value == "minimaps":
+ minimapsDir = node.attributes["value"].value
+ elif node.attributes["name"].value == "wallpapers":
+ wallpapersDir = node.attributes["value"].value
+ elif node.attributes["name"].value == "wallpaperFile":
+ wallpaperFile = node.attributes["value"].value
+ elif node.attributes["name"].value == "music":
+ musicDir = 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" and c != "S":
+ 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):
+ oldPalette = col[0] == "#"
+ if oldPalette == False and col[0] != "@":
+ showMsg(id, "incorrect dye colors: " + text, src, iserr)
+ return False
+
+ if oldPalette:
+ 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 testDyeChannel(file, color, text, iserr):
+ if len(color) < 1:
+ showMsgSprite(file, "dye channel size to small:" + text, iserr)
+ return -1
+ colors = dyesplit1.split(color)
+ for c in colors:
+ if len(c) != 1:
+ showMsgSprite(file, "dye channel 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" and c != "S":
+ showMsgSprite(file, "dye make incorrect: " + text, iserr)
+ continue
+ return len(colors)
+
+
+def testSprites(id, node, checkGender, isNormalDye, isMust, checkAction, 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
+
+ if isMust == False:
+ nosprite = True
+
+ 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 or len(sprites[0].childNodes) == 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, isNormalDye, checkAction, iserr)
+ else:
+ male = False
+ female = False
+ unisex = 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":
+ unisex = True
+ try:
+ variant = int(sprite.attributes["variant"].value)
+ except:
+ variant = 0
+ testSprite(id, file, variant, isNormalDye, checkAction, iserr)
+ if checkGender:
+ if male == False and unisex == False:
+ showMsg(id, "no male sprite tag", "",iserr)
+ if female == False and unisex == False:
+ showMsg(id, "no female sprite tag", "", iserr)
+ if unisex == True and female == True and male == True:
+ showMsg(id, "gender sprite tag with unisex tag", "", iserr)
+ if unisex == False and male == False and female == False:
+ showMsg(id, "no any gender tags", "", iserr)
+
+def testSprite(id, file, variant, isNormalDye, checkAction, iserr):
+ global safeDye
+ 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:
+ if not isNormalDye and color is not None and len(color) > 0:
+ showMsg(id, "sprite tag have dye string but it should not, because used colors dye", color, iserr)
+
+ oldSafe = safeDye
+ safeDye = True
+ testSpriteFile(id, fullPath, file, spritesDir + file2, dnum, variant, checkAction, iserr)
+ safeDye = oldSafe
+
+def powerOfTwo(num):
+ val = 1
+ while val < num:
+ val = val * 2
+ return val
+
+def testSpriteFile(id, fullPath, file, fileLoc, dnum, variant, checkAction, iserr):
+ global safeDye
+
+ try:
+ dom = minidom.parse(fullPath)
+ except:
+ return
+
+ if len(dom.childNodes) < 1:
+ return
+
+ try:
+ variants = dom.documentElement.attributes["variants"].value
+ except:
+ variants = 0
+
+# try:
+# variant_offset = dom.documentElement.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)
+ return
+ isets = set()
+ imagesetnums = dict()
+ num = 0
+ for imageset in imagesets:
+ try:
+ name = imageset.attributes["name"].value
+ except:
+ showMsgSprite(fileLoc, "imageset don't have name attribute", iserr)
+ name = None
+
+ if name is not None:
+ if name in isets:
+ showMsgSprite(fileLoc, "imageset with name '" + name + "' already exists", iserr)
+ isets.add(name)
+
+ image = ""
+ 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)
+ continue
+
+ try:
+ width = imageset.attributes["width"].value
+ except:
+ showMsgSprite(fileLoc, "no width attribute", iserr)
+ continue
+
+ try:
+ height = imageset.attributes["height"].value
+ except:
+ showMsgSprite(fileLoc, "no height attribute", iserr)
+
+ if imagecolor != "":
+ num = testDyeChannel(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)
+ continue
+ sizes = testImageFile(image, fullPath, 0, " " + fileLoc, iserr)
+ s1 = int(sizes[0] / int(width)) * int(width)
+
+ sizesOGL = [0,1]
+ sizesOGL[0] = powerOfTwo(sizes[0])
+ sizesOGL[1] = powerOfTwo(sizes[1])
+
+ if s1 == 0:
+ tmp = int(width)
+ else:
+ tmp = s1
+ if sizes[0] != s1 and tmp != sizesOGL[0] and sizes[0] != sizesOGL[0]:
+ if silent != True:
+ showMsgSprite(fileLoc, "image width " + str(sizes[0]) + \
+ " (need " + str(tmp) + ") is not multiply to frame size " + width + ", image:" + image, False)
+
+ if sizes[0] != sizesOGL[0]:
+ if sizesOGL[0] > sizes[0]:
+ txt = str(sizesOGL[0] / 2) + " or "
+ else:
+ txt = ""
+
+ if showAll is True:
+ showMsgSprite(fileLoc, "image width should be power of two. If not image will be resized on the fly."\
+ "\nCurrent image width " + str(sizes[0]) + \
+ ". used in sprite width " + str(tmp) +
+ "\nallowed width " + txt + str(sizesOGL[0]) + " (" + image + ")", False)
+
+ s2 = int(sizes[1] / int(height)) * int(height)
+
+ if s2 == 0:
+ tmp = int(height)
+ else:
+ tmp = s2;
+
+ if sizes[1] != s2 and tmp != sizesOGL[1] and sizes[1] != sizesOGL[1]:
+ if silent != True:
+ showMsgSprite(fileLoc, "image height " + str(sizes[1]) + \
+ " (need " + str(tmp) + ") is not multiply to frame size " + height + ", image:" + image, False)
+
+ if sizes[1] != sizesOGL[1]:
+ if sizesOGL[1] > sizes[1]:
+ txt = str(sizesOGL[1] / 2) + " or "
+ else:
+ txt = ""
+
+ if showAll is True:
+ showMsgSprite(fileLoc, "image height should be power of two. If not image will be resized on the fly."\
+ "\nCurrent image height " + str(sizes[1]) + \
+ ". used in sprite height " + str(tmp) +
+ "\nallowed height " + txt + str(sizesOGL[1]) + " (" + 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 > 0 and 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: " + image, iserr)
+ if name is not None and num > 0:
+ imagesetnums[name] = num
+
+ try:
+ includes = dom.getElementsByTagName("include")
+ for include in includes:
+ try:
+ incfile = include.attributes["file"].value
+ file2 = os.path.abspath(parentDir + os.path.sep + spritesDir + incfile)
+ if not os.path.isfile(file2):
+ showMsgSprite(fileLoc, "include file not exists " + incfile, True)
+ except:
+ showMsgSprite(fileLoc, "bad include", iserr)
+
+
+ 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()
+ hpSet = set()
+ for action in actions:
+ try:
+ name = action.attributes["name"].value
+ except:
+ showMsgSprite(fileLoc, "no action name", iserr)
+ continue
+ try:
+ hp = action.attributes["hp"].value
+ except:
+ hp = "100"
+ try:
+ setname = action.attributes["imageset"].value
+ except:
+ setname = ""
+ if setname in imagesetnums:
+ num = imagesetnums[setname]
+ else:
+ num = 0
+ showMsgSprite(fileLoc, "using incorrect imageset name in action: " + name, iserr)
+ frameSet = frameSet | testSpriteAction(fileLoc, name, action, num, iserr)
+
+ if name + "|" + hp in actset:
+ showMsgSprite(fileLoc, "duplicate action: " + name, iserr)
+ continue
+ actset.add(name + "|" + hp)
+ hpSet.add(hp)
+
+ 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:
+ if silent != True:
+ showMsgSprite(fileLoc, "unused frames: " + errIds[0:len(errIds)-1], False)
+
+ if checkAction != "":
+ for hp in hpSet:
+ if checkAction + "|" + hp not in actset:
+ showMsgSprite(fileLoc, "no attack action '" + checkAction + "' in sprite", iserr)
+
+
+def testSpriteAction(file, name, action, numframes, iserr):
+ framesid = set()
+
+ try:
+ animations = action.getElementsByTagName("animation")
+ except:
+ animations = None
+
+ if animations == None or len(animations) == 0:
+ if name != "default":
+ showMsgSprite(file, "no animation tags in action: " + name, False)
+ else:
+ return framesid
+
+ aniset = set()
+ delayTags = ("frame", "sequence", "pause")
+
+ for animation in animations:
+ lastAttack = None
+ 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
+ labels = set()
+
+ for node2 in animation.childNodes:
+ if name == "attack" and node2.nodeName != "#text":
+ lastAttack = node2.nodeName
+ if node2.nodeName in delayTags:
+ try:
+ delay = int(node2.attributes["delay"].value)
+ except:
+ delay = 0
+
+ if delay % 10 != 0 and showAll is True:
+ showMsgSprite(file, "delay " + str(delay) + " must be multiple of 10 in action: " + name + \
+ ", direction: " + direction, False)
+
+
+ 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) + \
+ " action: " + 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 + "\n" + node2.toxml(), False)
+ #print node2.toxml()
+ else:
+ lastIndex1 = idx
+ lastIndex2 = -1
+ lastOffsetX = offsetX
+ lastOffsetY = offsetY
+
+ framesid.add(idx)
+ cnt = cnt + 1
+ elif node2.nodeName == "sequence":
+ sequence = node2
+ try:
+ sframes = dyesplit2.split(sequence.attributes["value"].value)
+ except:
+ sframes = None
+ if sframes is not None:
+ for frm in sframes:
+ if frm != "p":
+ k = frm.find("-")
+ if k == 0 or k == len(frm) - 1:
+ showMsgSprite(file, "incorrect sequence value " + \
+ name + ", direction: " + direction, iserr)
+ elif k == -1:
+ #same as frame
+ idx = int(frm)
+ if idx >= numframes or idx < 0:
+ showMsgSprite(file, "incorrect frame index " + str(idx) + \
+ " action: " + name + ", direction: "\
+ + direction, iserr)
+ else:
+ framesid.add(idx)
+ else:
+ #same as simple sequence
+ i1 = int(frm[:k])
+ i2 = int(frm[k + 1:])
+ 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)
+
+ for i in range(i1,i2 + 1):
+ framesid.add(i)
+ cnt = cnt + 1
+ continue
+
+ 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)
+# try:
+# repeat = int(sequence.attributes["repeat"].value)
+# except:
+# repeat = 1
+
+ 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. May be need use repeat attribue? for start=" \
+ + str(i1) + ", end=" + str(i2) + " action: " + \
+ name + ", direction: " + direction + "\n" + node2.toxml(), False)
+ else:
+ lastIndex1 = i1
+ lastIndex2 = i2
+ lastOffsetX = offsetX
+ lastOffsetY = offsetY
+
+ cnt = cnt + 1
+ for i in range(i1,i2 + 1):
+ framesid.add(i)
+ elif node2.nodeName == "end" or node2.nodeName == "jump" or node2.nodeName == "label" or node2.nodeName == "goto":
+ lastIndex1 = -1
+ lastIndex2 = -1
+ lastOffsetX = 0
+ lastOffsetY = 0
+ cnt = cnt + 1
+ elif node2.nodeName == "pause":
+ try:
+ delay = int(node2.attributes["delay"].value)
+ except:
+ delay = 0
+ if delay <= 0:
+ showMsgSprite(file, "incorrect delay in pause tag " + name, iserr)
+
+ elif node2.nodeName == "#text" or node2.nodeName == "#comment":
+ None
+ else:
+ showMsgSprite(file, "unknown animation tag: " + node2.nodeName + ", " + name, False)
+
+ if node2.nodeName == "jump":
+ try:
+ jaction = node2.attributes["action"].value
+ except:
+ jaction = ""
+ if jaction == "" or jaction is None:
+ showMsgSprite(file, "no action attribute in jump tag " + name, iserr)
+ elif node2.nodeName == "label":
+ try:
+ label = node2.attributes["name"].value
+ except:
+ label = ""
+ if label == "" or label is None:
+ showMsgSprite(file, "no name attribute in label tag " + name, iserr)
+ else:
+ if label in labels:
+ showMsgSprite(file, "duplicate label " + label + " " + name + "\n" \
+ + node2.toxml(), iserr)
+ else:
+ labels.add(label)
+ elif node2.nodeName == "goto":
+ try:
+ label = node2.attributes["label"].value
+ except:
+ label = ""
+ if label == "" or label is None:
+ showMsgSprite(file, "no label attribute in goto tag " + name, iserr)
+ if cnt == 0:
+ showMsgSprite(file, "no frames or sequences in action: " + name, iserr)
+
+ if name == "attack":
+ if lastAttack is not None and lastAttack != "end":
+ showMsgSprite(file, "last attack tag should be <end/> or attack animation "\
+ "can be infinite. direction: " + direction, False)
+
+
+ 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, src, iserr):
+ try:
+ img = Image.open(fullPath, "r")
+ img.load()
+ except:
+ showMsgFile(file, "incorrect image format" + src, iserr)
+ return
+
+ if img.format != "PNG":
+ showMsgFile(file, "image format is not png" + src, 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, sfxDir, msg):
+ fullPath = parentDir + "/" + sfxDir + file
+ if not os.path.isfile(fullPath) or os.path.exists(fullPath) == False:
+ print "error:" + fullPath
+ if msg != "":
+ showMsgFile(file, "sound file not found: " + msg , True)
+ else:
+ showMsgFile(file, "sound file not found", True)
+ return
+ try:
+ 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
+ try:
+ dom = minidom.parse(fullPath)
+ except:
+ showMsgFile(file, "incorrect particle xml file", True)
+ return
+
+ 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]
+ if imagecolor != None and len(imagecolor) > 0:
+ testDye(id, imagecolor, "image=" + image, src, True)
+ fullName = parentDir + "/" + image
+ if not os.path.isfile(fullName) or os.path.exists(fullName) == False:
+ showMsgFile(file, "image file not exist: " + image, True)
+ else:
+ testImageFile(image, fullName, 0, " " + file,True)
+ for node in parentNode.getElementsByTagName("emitter"):
+ testEmitters(id, file, node, src)
+
+
+
+def testItems(fileName, imgDir):
+ global warnings, errors, safeDye
+ if not stfu:
+ print "Checking " + fileName
+ try:
+ dom = minidom.parse(parentDir + "/" + fileName)
+ except Exception as err:
+ print "error: " + fileName + ": corrupted"
+ print err
+ errors = errors + 1
+ return
+ idset = set()
+ oldId = None
+ for node in dom.documentElement.childNodes:
+ if node.nodeName == "include":
+ try:
+ name = node.attributes["name"].value
+ if name == "":
+ errors = errors + 1
+ print "error: " + fileName + ": Empty include name";
+ testItems(name, imgDir)
+ except:
+ errors = errors + 1
+ print "error: " + fileName + ": Broken include tag";
+ continue
+ if node.nodeName != "item":
+ continue
+
+ if node.parentNode != dom.documentElement:
+ continue
+
+ try:
+ id = node.attributes["id"].value
+ except:
+ if oldId is None:
+ print "error: " + fileName + ": item without id"
+ else:
+ print "error: " + fileName + ": item without id. Last id was: " + oldId
+ errors = errors + 1
+ continue
+ oldId = id
+ if id in idset:
+ print "error: " + fileName + ": duplicated id=" + id
+ errors = errors + 1
+ else:
+ idset.add(id)
+
+ idI = int(id)
+
+ try:
+ colors = node.attributes["colors"].value
+ except:
+ colors = None
+
+ try:
+ type = node.attributes["type"].value
+ except:
+ type = ""
+ print "warn: " + fileName + ": 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 = ""
+
+ try:
+ drawBefore = node.attributes["drawBefore"].value
+ except:
+ drawBefore = ""
+
+ try:
+ drawAfter = node.attributes["drawAfter"].value
+ except:
+ drawAfter = ""
+
+# try:
+# drawPriority = int(node.attributes["drawPriority"].value)
+# except:
+# drawPriority = 0
+
+ if type == "hairsprite":
+ if idI >= 0:
+ print "error: " + fileName + ": hairsprite with id=" + id
+ errors = errors + 1
+ elif idI < -100:
+ print "error: " + fileName + ": hairsprite override player sprites"
+ errors = errors + 1
+
+ safeDye = True
+ testSprites(id, node, True, True, True, "", True)
+ safeDye = False
+
+ elif type == "racesprite":
+ if idI >= 0:
+ print "error: " + fileName + ": racesprite with id=" + id
+ errors = errors + 1
+ elif idI > -100:
+ print "error: " + fileName + ": 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" or type == "card":
+ if image == "":
+ print "error: " + fileName + ": missing image attribute on id=" + id
+ errors = errors + 1
+ continue
+ elif len(imagecolor) > 0:
+ if colors is None:
+ testDye(id, imagecolor, "image=" + image0, fileName, True)
+ else:
+ testDyeChannel(id, imagecolor, "image=" + image0, True)
+ if colors not in colorsList:
+ print "error: " + fileName + ": colors value " + colors + " not found in itemcolors.xml"
+ errors = errors + 1
+
+ if floorcolor != None and len(floorcolor) > 0:
+ if colors is None:
+ testDye(id, floorcolor, "floor=" + floor0, fileName, True)
+ else:
+ testDyeChannel(id, imagecolor, "floor=" + floor0, True);
+ if colors not in colorsList:
+ print "error: " + fileName + ": colors value " + colors + " not found in itemcolors.xml"
+ errors = errors + 1
+
+ if description == "":
+ print "warn: " + fileName + ": missing description attribute on id=" + id
+ warnings = warnings + 1
+ elif description == ".":
+ print "warn: " + fileName + ": broken description attribute on id=" + id
+ warnings = warnings + 1
+
+ if missile != "":
+ testParticle(id, missile, fileName)
+
+ testSounds(id, node, "item")
+
+ try:
+ floorSprite = node.getElementsByTagName("floor")[0]
+ except:
+ floorSprite = None
+ if floorSprite != None:
+ if floor != None:
+ print "error: " + fileName + ": found attribute floor and tag floor. " + \
+ "Should be only one tag or attribute. id=" + id
+ errors = errors + 1
+ testSprites(id, floorSprite, False, colors is None, True, "", 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)
+ errors = errors + 1
+ else:
+ testImageFile(imgDir + floor, fullPath, 0, "", True)
+
+ testItemReplace(id, node, "replace")
+ if drawBefore != "":
+ checkSpriteName(id, drawBefore)
+ if drawAfter != "":
+ checkSpriteName(id, drawAfter)
+
+ try:
+ attackaction = node.attributes["attack-action"].value
+ except:
+ attackaction = ""
+
+ testSprites(id, node, True, colors is None, False, attackaction, 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, colors is None, True, "", err)
+ elif type == "other":
+ None
+ elif type != "":
+ print "warn: " + fileName + ": unknown type '" + type + "' for id=" + id
+ warnings = warnings + 1
+
+
+def testItemReplace(id, rootNode, name):
+ global warnings, errors
+ for node in rootNode.getElementsByTagName(name):
+ if node.parentNode != rootNode:
+ continue
+ try:
+ sprite = node.attributes["sprite"].value
+ except:
+ if len(node.attributes) != 0:
+ print "error: reading replace sprite name, id=" + str(id)
+ errors = errors + 1
+ continue
+ checkSpriteName(id, sprite)
+ for itemNode in node.getElementsByTagName("item"):
+ if itemNode.parentNode != node:
+ continue
+ #TODO here need check "from" and "to" for correct item id
+
+
+def checkSpriteName(id, name):
+ global warnings, errors
+ if name != "race" and name != "type" and name != "shoes" and name != "boot" and \
+ name != "boots" and name != "bottomclothes" \
+ and name != "bottom" and name != "pants" and name != "topclothes" and \
+ name != "top" and name != "torso" and name != "body" and name != "misc1" \
+ and name != "misc2" and name != "scarf" and name != "scarfs" and \
+ name != "hair" and name != "hat" and name != "hats" and name != "wings" \
+ and name != "glove" and name != "gloves" and name != "weapon" and \
+ name != "weapons" and name != "shield" and name != "shields" and \
+ name != "amulet" and name != "amulets" and name != "ring" and name != "rings":
+ print "error: unknown sprite name " + name + ", id=" + str(id)
+ errors = errors + 1
+
+
+def testMonsters(fileName):
+ global warnings, errors
+ if not stfu:
+ print "Checking " + fileName
+ dom = minidom.parse(parentDir + "/" + fileName)
+ idset = set()
+ for node in dom.documentElement.childNodes:
+ if node.nodeName == "include":
+ try:
+ name = node.attributes["name"].value
+ if name == "":
+ errors = errors + 1
+ print "error: " + fileName + ": Empty include name";
+ testMonsters(name)
+ except:
+ errors = errors + 1
+ print "error: " + fileName + ": Broken include tag";
+ continue
+ if node.nodeName == "monster":
+ try:
+ id = node.attributes["id"].value
+ except:
+ print "error: " + fileName + ": no id for monster"
+ errors = errors + 1
+ continue
+
+ if id in idset:
+ print "error: " + fileName + ": duplicate id=" + id
+ errors = errors + 1
+ else:
+ idset.add(id)
+
+ try:
+ name = node.attributes["name"].value
+ except:
+ print "error: " + fileName + ": no name for id=" + id
+ errors = errors + 1
+ name = ""
+
+ testTargetCursor(id, node, fileName)
+ testSprites(id, node, False, True, True, "", 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, True)
+ 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
+ havemiss = False
+ 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" \
+ and event != "move" and event != "sit" and event != "spawn":
+ print "error: incorrect sound event name " + event + " in id=" + id
+ errors = errors + 1
+ elif type == "item":
+ if event != "hit" and event != "strike" and event != "miss":
+ print "error: incorrect sound event name " + event + " in id=" + id
+ errors = errors + 1
+ if event == "strike" or event == "miss":
+ if havemiss:
+ print "error: miss and strike attributes at same time in id=" + id
+ errors = errors + 1
+ havemiss = True
+
+ testSound(sound.childNodes[0].data, sfxDir, "")
+
+def testNpcs(file):
+ global warnings, errors
+ if not stfu:
+ print "Checking " + file
+ dom = minidom.parse(parentDir + "/" + file)
+ idset = set()
+ for node in dom.documentElement.childNodes:
+ if node.nodeName == "include":
+ try:
+ name = node.attributes["name"].value
+ if name == "":
+ errors = errors + 1
+ print "error: " + file + ": Empty include name";
+ testNpcs(name)
+ except:
+ errors = errors + 1
+ print "error: " + file + ": Broken include tag";
+ continue
+ if node.nodeName != "npc":
+ continue
+
+ try:
+ id = node.attributes["id"].value
+ except:
+ print "error: " + file + ": no id for npc"
+ errors = errors + 1
+ continue
+
+ if id in idset:
+ print "error: " + file + ": duplicate npc id=" + id
+ errors = errors + 1
+ else:
+ idset.add(id)
+
+ testSprites(id, node, False, True, True, "", 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 readAttr2(node, attr, dv):
+ try:
+ return node.attributes[attr].value
+ except:
+ return dv
+
+
+def testMap(mapName, 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)
+ mapVersion = readAttr(root, "version", "1.0", "error: missing map version: " + file, True)
+
+ if mapWidth == 0 or mapHeight == 0 or mapTileWidth == 0 or mapTileHeight == 0:
+ return
+
+ mapVersion = mapVersion.replace(".", "")
+ try:
+ mapVersion = int(mapVersion)
+ except:
+ showMsgFile(file, "Invalid map version: " + str(mapVersion), False)
+
+ if mapVersion < tiledVersion:
+ showMsgFile(file, "Outdated map version: " + str(mapVersion), False)
+
+ if mapWidth < borderSize * 2 + 1:
+ if silent == False or file.find("maps/test") != 0:
+ showMsgFile(file, "map width to small: " + str(mapWidth), False)
+ if mapHeight < borderSize * 2 + 1:
+ if silent == False or file.find("maps/test") != 0:
+ showMsgFile(file, "map height to small: " + str(mapHeight), False)
+
+ if len(dom.getElementsByTagName("properties")) < 1:
+ showMsgFile(file, "missing map properties", True)
+ return
+
+ for props in dom.getElementsByTagName("properties"):
+ for prop in props.getElementsByTagName("property"):
+ try:
+ name = prop.attributes["name"].value
+ except:
+ name = ""
+ if name == "":
+ showMsgFile(file, "wrong property", True)
+ continue
+ try:
+ value = prop.attributes["value"].value
+ except:
+ value = ""
+ if value == "" and name == "name":
+ showMsgFile(file, "empty map name property", True)
+ continue
+
+ # Total minimum required width
+ if mapWidth < 60:
+ name1=file.find("maps/test")
+ name2=file.find("maps/000-1")
+ if name1 == 0 or name2 == 0:
+ pass
+ else:
+ showMsgFile(file, "total map width to small: " + str(mapWidth), False)
+
+ tilesMap = dict()
+
+ for tileset0 in dom.getElementsByTagName("tileset"):
+ tileset = tileset0
+ try:
+ firstGid = int(tileset.attributes["firstgid"].value)
+ except:
+ firstGid = 0
+
+ try:
+ source = tileset.attributes["source"].value
+ if source is not None and source != "":
+ file2 = os.path.abspath(parentDir + os.path.sep + mapsDir + source)
+ if not os.path.isfile(file2):
+ showMsgFile(file, "missing source file in tileset " + source, True)
+ except:
+ source = ""
+
+ tile = Tileset()
+ tile.firstGid = firstGid
+ tile.lastGid = 0
+
+ if source[-4:] == ".tsx":
+ relativePath = parentDir + "/" + mapsDir + source
+ try:
+ dom2 = minidom.parse(relativePath)
+ tileset = dom2.documentElement
+ idx = relativePath.rfind("/")
+ relativePath = relativePath[:idx+1]
+ relativePath2 = source
+ idx = relativePath2.rfind("/")
+ relativePath2 = relativePath2[:idx+1]
+ except:
+ showMsgFile(file, "tsx not found: " + source, True)
+ relativePath = ""
+ relativePath2 = ""
+ else:
+ relativePath = ""
+ relativePath2 = ""
+
+ 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)
+ if firstGid in tilesMap:
+ showMsgFile(file, "tile with firstgid " + str(firstGid) + \
+ " already exist: " + name + ", " + file, True)
+ continue
+
+ tile.width = tileWidth
+ tile.tileWidth = tileWidth
+ tile.height = tileHeight
+ tile.tileHeight = tileHeight
+ tile.name = name
+
+ 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
+
+ image = images[0]
+ source = readAttr(image, "source", None, "error: missing source in image tag in tile " \
+ + name + ": " + file, True)
+
+ if source != None:
+ if relativePath == "":
+ imagePath = os.path.abspath(parentDir + "/" + mapsDir + source)
+ else:
+ imagePath = os.path.abspath(relativePath + 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:
+ if s1 == 0:
+ s1 = int(tileWidth)
+ 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 + (int(width / int(tileWidth)) * int(height / int(tileHeight))) - 1
+ if height != s2:
+ if s2 == 0:
+ s2 = int(tileHeight)
+ showMsgFile(file, "image width " + str(height) + \
+ " (need " + str(s2) + ") is not multiply to tile size " + \
+ str(tileHeight) + ". " + source + ", " + name, False)
+
+ tile.source = relativePath2 + source
+ # hack to change relative back path to normal relative path
+ if len(tile.source) > 3 and tile.source[:11] == "../graphics":
+ tile.source = tile.source[3:]
+ tilesMap[tile.firstGid] = tile
+
+ #if mapName not in mapToAtlas:
+ # showMsgFile(file, "map dont have atlas", True)
+
+ tileset = tileset0
+
+ testTiles(mapName, file, tilesMap)
+ layers = dom.getElementsByTagName("layer")
+ objects = dom.getElementsByTagName("object")
+ if layers == None or len(layers) == 0:
+ showMsgFile(file, "map dont have layers", True)
+ return
+
+ fringe = None
+ collision = None
+ lowLayers = []
+ overLayers = []
+ beforeFringe = True
+ haveHeight = False
+
+ for layer in layers:
+ name = readAttr(layer, "name", None, "layer dont have name", True)
+ if name == None:
+ continue
+ if name.lower() == "height" or name.lower() == "heights":
+ haveHeight=True
+ 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.append(obj)
+ else:
+ overLayers.append(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)
+ testOverSizedTiles(obj, tilesMap, file)
+
+ if fringe == None:
+ showMsgFile(file, "missing fringe layer", True)
+ if collision == None:
+ showMsgFile(file, "missing collision layer", True)
+ elif mapName != "test.tmx" and mapName != "testbg.tmx":
+ ids = testCollisionLayer(file, collision, tilesMap)
+ if ids[0] != None and len(ids[0]) > 0:
+ if silent == False or file.find("maps/test") != 0:
+ showLayerErrors(file, ids[0], "empty tiles in collision border", False)
+ if ids[1] != None and len(ids[1]) > 0:
+ if silent == False or file.find("maps/test") != 0:
+ showLayerErrors(file, ids[1], "incorrect tileset index in collision layer", False)
+
+ if len(lowLayers) < 1:
+ showMsgFile(file, "missing low layers", False)
+ if len(overLayers) < 1:
+ if (silent == False or file.find("maps/test") != 0) and herc == False:
+ showMsgFile(file, "missing over layers", False)
+
+ if not haveHeight:
+ showMsgFile(file, "missing height layer", False)
+
+ if fringe != None:
+ lowLayers.append(fringe)
+ warn1 = None
+
+ if len(overLayers) > 0:
+ testData = dict()
+ warn1 = testLayerGroups(file, lowLayers, collision, None, tilesMap, False)
+ lowLayers.extend(overLayers)
+ err1 = testLayerGroups(file, lowLayers, collision, testData, tilesMap, False)
+ reportAboutTiles(file, testData)
+ else:
+ testData = dict()
+ err1 = testLayerGroups(file, lowLayers, collision, testData, tilesMap, False)
+ reportAboutTiles(file, testData)
+
+ if warn1 != None and err1 != None:
+ warn1 = warn1 - err1
+ if warn1 != None and len(warn1) > 0:
+ if silent != True:
+ 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)
+
+ for objx in objects:
+ x = readAttr(objx, "x", 0, "object in invalid X position", False)
+ y = readAttr(objx, "y", 0, "object in invalid Y position", False)
+ w = readAttr2(objx, "width", 0)
+ h = readAttr2(objx, "height", 0)
+ try:
+ fs=False
+ if (float(x) != int(x)):
+ showMsgFile(file, "Invalid object X pos - must be integer", False)
+ fs=1
+ if (float(y) != int(y)):
+ showMsgFile(file, "Invalid object Y pos - must be integer", False)
+ fs=1
+ if (float(w) != int(w)):
+ showMsgFile(file, "Invalid object Width - must be integer", False)
+ fs=1
+ if (float(h) != int(h)):
+ showMsgFile(file, "Invalid object Height - must be integer", False)
+ fs=1
+ if fs:
+ id1=readAttr(objx, "id", "?", "invalid object ID", False)
+ name1=readAttr(objx, "name", "?", "invalid object name", False)
+ type1=readAttr(objx, "type", "?", "invalid object type", False)
+ showMsgFile(file, "Broken object: id %s name %s (%s,%s,%s,%s) type %s" % (id1, name1, x, y, w, h, type1), True);
+ except:
+ id1=readAttr(objx, "id", "?", "invalid object ID", False)
+ showMsgFile(file, "Broken object ID %s - x/y/h/w corrupted data detected" % id1, True)
+
+def testOverSizedTiles(layer, tiles, file):
+ global warnings, errors
+
+ oversizeErrList = []
+ ignoreErrList = []
+ ignoreTilesetList = set()
+ ignoredFiles = []
+ if "ignored" in atlasToFiles:
+ ignoredFiles = atlasToFiles["ignored"]
+ for x in range(0, layer.width):
+ for y in range(0, layer.height):
+ idx = ((y * layer.width) + x) * 4
+ val = getLDV(layer.arr, idx)
+ if val == 0:
+ continue
+
+ tile, tilesetName = findTileByGid(tiles, val)
+ if layer.name.lower() not in ("collision", "heights", "height") and tilesetName in ignoredFiles:
+ ignoreErrList.append((x, y))
+ ignoreTilesetList.add(tilesetName)
+ if layer.name.lower() == "fringe":
+ continue
+ if tile is None:
+ # now ignoring, this happend because layer parser
+ # not support includes
+ None
+ elif tile.tileWidth > 32 and x + 1 < layer.width:
+ for x2 in range(x + 1, x + 1 + int(tile.width / 32), 1):
+ idx = ((y * layer.width) + x2) * 4
+ val = getLDV(layer.arr, idx)
+ tile, _ = findTileByGid(tiles, val)
+ if val > 0:
+ oversizeErrList.append((x, y))
+ if silent != True:
+ warnings = warnings + 1
+ elif tile.tileHeight > 32 and y - 1 > 0:
+ for y2 in range(y - 1, y - 1 - int(tile.height / 32), -1):
+ idx = ((y2 * layer.width) + x) * 4
+ val = getLDV(layer.arr, idx)
+ tile, _ = findTileByGid(tiles, val)
+ if val > 0:
+ oversizeErrList.append((x, y))
+ if silent != True:
+ warnings = warnings + 1
+
+ if len(oversizeErrList) > 0 and silent != True:
+ print "error: " + file + ": Oversized tile overlapped to next tile in layer " + layer.name + \
+ ". Possible incorrect map drawing"
+ errors = errors + 1
+ errStr = ""
+ k = 0
+ for err in oversizeErrList:
+ errStr = errStr + str(err) + ", "
+ k = k + 1
+ if k > 100:
+ errStr = errStr + "..."
+ break
+ print errStr
+
+ if len(ignoreErrList) > 0:
+ errStr = ""
+ for err in ignoreTilesetList:
+ if errStr != "":
+ errStr = errStr + ", "
+ errStr = errStr + err
+ print("error: {0}: Tiles from ignored atlas used in layer {1}. Tilesets: {2}. "
+ "Possible incorrect map drawing".format(file, layer.name, errStr))
+ errors = errors + 1
+ errStr = ""
+ k = 0
+ for err in ignoreErrList:
+ errStr = errStr + str(err) + ", "
+ k = k + 1
+ if k > 100:
+ errStr = errStr + "..."
+ break
+ print errStr
+
+
+def testTiles(mapName, file, tilesMap):
+ ignoredFiles = []
+ if "ignored" in atlasToFiles:
+ ignoredFiles = atlasToFiles["ignored"]
+ for firstGid in tilesMap:
+ tile1 = tilesMap[firstGid]
+ if mapName in mapToAtlas:
+ atlasName = mapToAtlas[mapName]
+ if atlasName in atlasToFiles:
+ files = atlasToFiles[atlasName]
+ """
+ if tile1.source not in files and tile1.source not in ignoredFiles:
+ showMsgFile(file, "tileset '{0} ({1})' not present in atlas '{2}'".format(
+ tile1.name,
+ tile1.source,
+ atlasName),
+ True)
+ """
+ for gid2 in tilesMap:
+ if firstGid != gid2:
+ tile2 = tilesMap[gid2]
+ if (tile1.firstGid >= tile2.firstGid and tile1.firstGid <= tile2.lastGid) or \
+ (tile1.lastGid >= tile2.firstGid and tile1.lastGid <= tile2.lastGid):
+ showMsgFile(file, "overlaping tilesets gids \"" + tile1.name \
+ + "\" and \"" + tile2.name + "\"", True)
+
+
+def reportAboutTiles(file, data):
+ if testBadCollisions == False:
+ return
+ for k in data:
+ d = data[k]
+ if d[0] != 0 and d[2] != 0:
+ #print file + ": " + str(k) + ": " + str(d)
+ testCollisionPoints(file, k, d, 1, 3, \
+ "possible tiles should be without collision: ", \
+ "because no collision: ", False)
+ testCollisionPoints(file, k, d, 3, 1, \
+ "possible tiles should be with collision: ", \
+ "because collision: ", False)
+
+
+def testCollisionPoints(file, tileId, data, idx1, idx2, msg1, msg2, iserr):
+ #print "test: " + str(idx1) + ", " + str(idx2)
+ cnt1 = 0
+ cnt2 = 0
+ for point in data[idx1]:
+ if point[2] > 0:
+ cnt1 = cnt1 + 1
+ for point in data[idx2]:
+ if point[2] > 0:
+ cnt2 = cnt2 + 1
+
+ ln1 = len(data[idx1])
+ ln2 = len(data[idx2])
+ #print "cnt1=" + str(cnt1) + ", cnt2=" + str(cnt2) + ", ln1=" + str(ln1) + ", ln2=" + str(ln2)
+ if ln1 > 0 and ln2 > 0 and cnt2 > 0 and cnt2 < cnt1 - tileNumDiff and cnt2 < maxNumErrTiles:
+ text = msg1
+ c = 0
+ for point in data[idx2]:
+ if point[2] > 0:
+ if c > 100:
+ break
+ text = text + "(" + str(point[0]) + ", " + str(point[1]) + "), "
+ c = c + 1
+ text = text[:len(text)-2] + " " + msg2
+ c = 0
+ for point in data[idx1]:
+ if c > 100:
+ break
+ text = text + "(" + str(point[0]) + ", " + str(point[1]) + "), "
+ c = c + 1
+ showMsgFile(file, text[:len(text)-2], iserr)
+
+
+def testCollisionLayer(file, layer, tiles):
+ haveTiles = False
+ tileset = set()
+ badtiles = set()
+ arr = layer.arr
+ x1 = borderSize
+ y1 = borderSize
+ x2 = layer.width - borderSize
+ y2 = layer.height - borderSize
+ if x2 < 0:
+ x2 = 0
+ if y2 < 0:
+ y2 = 0
+
+ if arr is None :
+ return (set(), set())
+
+ for x in range(0, layer.width):
+ for y in range(0, layer.height):
+ idx = ((y * layer.width) + x) * 4
+ val = getLDV(arr, idx)
+ if val != 0:
+ haveTiles = True
+ tile, tilesetName = findTileByGid(tiles, val)
+ if tile is not None:
+ idx = val - tile.firstGid
+ if idx > 6: # 6 - max collision type
+ badtiles.add(((x, y), idx))
+ else:
+ badtiles.add(((x, y), "+{0}".format(val)))
+ if val == 0 and (x < x1 or x > x2 or y < y1 or y > y2):
+ tileset.add((x, y))
+
+ if haveTiles == False:
+ if silent == False or file.find("maps/test") != 0:
+ showMsgFile(file, "empty collision layer", False)
+ return (set(), set())
+
+ return (tileset, badtiles)
+
+
+def findTileByGid(tiles, gid):
+ for firstGid in tiles:
+ if firstGid <= gid:
+ tile = tiles[firstGid]
+ if tile.lastGid >= gid:
+ return (tile, tile.source)
+ return (None, None)
+
+
+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 getLDV2(arr, x, y, width, height, tilesMap):
+ ptr = ((y * width) + x) * 4
+ res = getLDV(arr, ptr)
+ yend = height - 1
+ if yend - y > 5:
+ yend = y + 5
+ for y2 in range(height - 1, y, -1):
+ x0 = x - 3
+ if x0 < 0:
+ x0 = 0
+ for x2 in range(x0, x + 1):
+ ptr = ((y2 * width) + x2) * 4
+ val = getLDV(arr, ptr)
+ tile, _ = findTileByGid(tilesMap, val)
+ if tile is not None:
+ if (tile.tileHeight > 32 or y2 == y) and (tile.tileWidth > 32 or x2 == x):
+ hg = tile.tileHeight / 32
+ wg = tile.tileWidth / 32
+ if (y2 - y < hg or y2 == y) and (x2 - x < wg or x2 == x):
+ res = val
+
+ return res
+
+
+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
+# print file
+# for item in arr:
+# print item
+ elif encoding == "csv":
+ if compression != "":
+ showMsgFile(file, "not supported compression " + compression + \
+ " for csv layer format:" + name, True)
+ binData = data.childNodes[0].data.strip()
+ f = StringIO.StringIO(binData)
+ arr = list(csv.reader(f, delimiter=',', quotechar='|'))
+ layer.arr = []
+# print file
+ for row in arr:
+ try:
+ for item in row:
+ if item != "":
+ nums = splitBytes(int(item))
+ layer.arr.append(nums[0])
+ layer.arr.append(nums[1])
+ layer.arr.append(nums[2])
+ layer.arr.append(nums[3])
+ except:
+ None
+
+ f.close()
+ arr = array.array('i', (layer.arr))
+ layer.arr = arr
+# for item in arr:
+# print item
+
+ elif encoding == "":
+ if compression != "":
+ showMsgFile(file, "not supported compression " + compression + \
+ " for xml layer format:" + name, True)
+
+ layer.arr = []
+ tiles = data.getElementsByTagName("tile")
+# print file
+ for tile in tiles:
+ try:
+ gid = int(tile.attributes["gid"].value)
+ except:
+ showMsgFile(file, "incorrect xml layer format: " + name, True)
+ return layer
+ nums = splitBytes(gid)
+ layer.arr.append(nums[0])
+ layer.arr.append(nums[1])
+ layer.arr.append(nums[2])
+ layer.arr.append(nums[3])
+
+ arr = array.array('i', (layer.arr))
+ layer.arr = arr
+# for item in arr:
+# print item
+
+
+ # here may be i should check is tiles correct or not, but i will trust to tiled
+ return layer
+
+
+def splitBytes(num):
+ i1 = int(num % 256)
+ i2 = int(((num % 65536) - i1) / 256)
+ i3 = int(((num % 16777216) - i2 - i1) / 65536)
+ i4 = int(((num % 4294967296) - i3 - i2 - i1) / 16777216)
+ return (i1, i2, i3, i4)
+
+def testLayerGroups(file, layers, collision, tileInfo, tilesMap, 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
+ lastTileId = 0
+ 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 testBadCollisions == True:
+ val = getLDV2(arr, x, y, layer.width, layer.height, tilesMap)
+ else:
+ val = 0
+ val1 = getLDV(arr, ptr)
+ if val1 != 0:
+ good = True
+ if val == val1 and testBadCollisions == True:
+ lastTileId = val
+ if good == False:
+ errset.add((x,y))
+ elif testBadCollisions == True and collision != None and tileInfo != None:
+ if lastTileId not in tileInfo:
+ tileInfo[lastTileId] = [0, set(), 0, set()]
+ ti = tileInfo[lastTileId]
+ flg = getLDV(collision.arr, ((y * collision.width) + x) * 4)
+ cnt = countCollisionsNear(collision, x, y)
+ k = 0
+ if flg > 0:
+ if cnt[1] < cnt[0] and cnt[0] - cnt[1] > 5:
+ k = 1
+ ti[2] = ti[2] + 1
+ ti[3].add((x, y, k))
+ else:
+ if cnt[0] > cnt[1] and cnt[0] - cnt[1] > 5:
+ k = 1
+ ti[0] = ti[0] + 1
+ ti[1].add((x, y, k))
+
+ return errset
+
+
+def countCollisionsNear(layer, x, y):
+ arr = layer.arr
+ x1 = x - 1
+ y1 = y - 1
+ x2 = x + 1
+ y2 = y + 1
+ col = 0
+ nor = 0
+
+ if x1 < 0:
+ x1 = 0
+ if x2 >= layer.width:
+ x2 = layer.width - 1
+ if y1 < 0:
+ y1 = 0
+ if y2 >= layer.height:
+ y2 = layer.height - 1
+
+ for f in range(x1, x2 + 1):
+ for d in range(y1, y2 + 1):
+ if f != x or d != y:
+ val = getLDV(arr, ((d * layer.width) + f) * 4)
+ if val == 0:
+ nor = nor + 1
+ else:
+ col = col + 1
+ return (nor, col)
+
+
+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(file, mapsDir + file, dir + file)
+
+def testDirExists(path):
+ global errors
+ fullName = parentDir + "/" + path
+ if not os.path.exists(fullName):
+ print "error: path '" + path + "' not exists."
+ errors = errors + 1
+ elif not os.path.isdir(fullName):
+ print "error: path '" + path + "' is incorrect directory."
+ errors = errors + 1
+
+def testDefaultFiles():
+ global warnings
+ print "Checking default files"
+ testDirExists(iconsDir)
+ testDirExists(spritesDir)
+ testDirExists(particlesDir)
+ testDirExists(minimapsDir)
+ testDirExists(mapsDir)
+ testDirExists(sfxDir)
+ if silent != True:
+ testDirExists(musicDir)
+ testDirExists(wallpapersDir)
+
+ testSprite("0", spriteErrorFile, 0, True, "", True)
+ testParticle("0", particlesDir + levelUpEffectFile, "levelUpEffectFile")
+ testParticle("0", particlesDir + portalEffectFile, "portalEffectFile")
+ fullName = parentDir + "/" + wallpapersDir + wallpaperFile
+ if not os.path.isdir(fullName) and os.path.exists(fullName):
+ testImageFile(wallpapersDir + wallpaperFile, fullName, 0, "", False)
+
+
+def testMinimapsDir():
+ global errors, warnings
+
+ print "Checking minimaps"
+ fullPath = parentDir + "/" + minimapsDir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ print "warn: minimaps dir not exist"
+ warnings = warnings + 1
+ return
+ for file in os.listdir(fullPath):
+ if filtimages.search(file):
+ fullName = parentDir + "/" + minimapsDir + file
+ testImageFile(minimapsDir + file, fullName, 0, "", True)
+
+
+def testImagesDir(imagesDir, sz):
+ global errors, warnings
+
+ fullPath = parentDir + "/" + imagesDir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ return
+ for file in os.listdir(fullPath):
+ file2 = fullPath + "/" + file
+ if file[0] == ".":
+ continue
+ if not os.path.isfile(file2):
+ testImagesDir(imagesDir + file + "/", sz)
+ if filtimages.search(file):
+ fullName = parentDir + "/" + imagesDir + file
+ testImageFile(imagesDir + file, fullName, sz, "", True)
+
+
+def testSpritesDir(dir):
+ global errors, warnings, safeDye
+
+ fullPath = parentDir + "/" + spritesDir + dir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ return
+
+ for file in os.listdir(fullPath):
+ file2 = fullPath + "/" + file
+ if file[0] == ".":
+ continue
+ if not os.path.isfile(file2):
+ testSpritesDir(dir + file + "/")
+ if filtimages.search(file):
+ fullName = parentDir + "/" + spritesDir + dir + file
+ testImageFile(spritesDir + dir, fullName, 0, spritesDir + dir + file, True)
+ elif filtxmls.search(file):
+ fullName = dir + file
+ safeDye = True
+ testSprite("0", dir + file, 0, True, "", True)
+ safeDye = False
+
+
+
+def testParticlesDir(dir):
+ global errors, warnings, safeDye
+
+ fullPath = parentDir + "/" + dir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ return
+ for file in os.listdir(fullPath):
+ file2 = fullPath + "/" + file
+ if file[0] == ".":
+ continue
+ if not os.path.isfile(file2):
+ testParticlesDir(dir + file + "/")
+ if filtimages.search(file):
+ fullName = parentDir + "/" + dir + file
+ testImageFile(dir + file, fullName, 0, "", True)
+ elif filtxmls.search(file):
+ fullName = dir + file
+ safeDye = True
+ testParticle("0", dir + file, "")
+ safeDye = False
+
+
+def testSoundsDir(dir, sfxDir):
+ global errors, warnings
+
+ fullPath = parentDir + "/" + sfxDir + dir
+ if not os.path.isdir(fullPath) or not os.path.exists(fullPath):
+ print "warn: directory " + sfxDir + " not exist"
+ warnings = warnings + 1
+ return
+ for file in os.listdir(fullPath):
+ file2 = fullPath + "/" + file
+ if file[0] == ".":
+ continue
+ if not os.path.isfile(file2):
+ testSoundsDir(dir + file + "/", sfxDir)
+ elif filtogg.search(file):
+ testSound(dir + file, sfxDir, "")
+
+
+def testItemColors(fileName):
+ global warnings, errors, safeDye, colorLists
+ print "Checking itemcolors.xml"
+ try:
+ dom = minidom.parse(parentDir + "/" + fileName)
+ except:
+ return
+
+ for node in dom.getElementsByTagName("list"):
+ if node.parentNode != dom.documentElement:
+ continue
+
+ try:
+ name = node.attributes["name"].value
+ except:
+ print "error: colors list dont have name"
+ errors = errors + 1
+ continue
+ if name in colorsList:
+ print "error: duplicate color list: " + name
+ errors = errors + 1
+ continue
+ colorsList.add(name)
+ colors = set()
+ names = set()
+ for colorNode in node.getElementsByTagName("color"):
+ if colorNode.parentNode != node:
+ continue
+ try:
+ id = colorNode.attributes["id"].value
+ except:
+ print "error: getting id in list: " + name
+ errors = errors + 1
+ continue
+ try:
+ colorName = colorNode.attributes["name"].value
+ except:
+ print "error: getting name in list: " + name
+ errors = errors + 1
+ continue
+ try:
+ colorDye = colorNode.attributes["value"].value
+ except:
+ print "error: getting color in list: " + name
+ errors = errors + 1
+ if id in colors:
+ print "error: color with id " + str(id) + " already in list: " + name
+ errors = errors + 1
+ else:
+ colors.add(id)
+ if colorName in names:
+ print "error: color with name \"" + colorName + "\" already in list: " + name
+ errors = errors + 1
+ else:
+ names.add(colorName)
+ testDyeColors(id, colorDye, colorDye, name, True)
+
+def loadMapAtlases(fileName):
+ mapToAtlas = dict()
+ atlasToFiles = dict()
+ try:
+ root = ElementTree.parse(parentDir + "/" + fileName).getroot()
+ except:
+ showMsgFile(fileName, "load xml error. Probably file missing", True)
+ return (mapToAtlas, atlasToFiles)
+
+ for node in root.findall("map"):
+ mapName = node.attrib["name"]
+ atlasNode = node.find("atlas")
+ if atlasNode == None:
+ continue
+ atlasName = atlasNode.attrib["name"]
+ mapToAtlas[mapName] = atlasName
+ for node in root.findall("atlas"):
+ atlasName = node.attrib["name"]
+ files = []
+ for fileNode in node.findall("file"):
+ fileName = fileNode.attrib["name"]
+ files.append(fileName)
+ atlasToFiles[atlasName] = files
+ for mapName in mapToAtlas:
+ atlasName = mapToAtlas[mapName]
+ if atlasName not in atlasToFiles:
+ showMsgFile(fileName, "atlas '{0}' assigned to map not present in maps.xml".format(atlasName), True)
+
+ return (mapToAtlas, atlasToFiles)
+
+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)
+
+
+if len(sys.argv) == 2:
+ if sys.argv[1] == "all":
+ showAll = True
+ elif sys.argv[1] == "silent":
+ silent = True
+ elif sys.argv[1] == "stfu":
+ silent = True
+ stfu = True
+ elif sys.argv[1] == "herc":
+ silent = True
+ herc = True
+
+showHeader()
+print "Detecting clientdata dir"
+detectClientData([".", "..", "../../clientdata", parentDir])
+print "Checking xml file syntax"
+enumDirs(parentDir)
+loadPaths()
+(mapToAtlas, atlasToFiles) = loadMapAtlases("/maps.xml")
+testDefaultFiles()
+testItemColors("/itemcolors.xml")
+testItems("/items.xml", iconsDir)
+testMonsters("/monsters.xml")
+testNpcs("/npcs.xml")
+testMaps(mapsDir)
+testMinimapsDir()
+print "Checking images dir"
+testImagesDir(wallpapersDir, 0)
+print "Checking icons dir"
+testImagesDir(iconsDir, 32)
+print "Checking sprites dir"
+testSpritesDir("")
+print "Checking particles dir"
+testParticlesDir(particlesDir)
+print "Checking sfx dir"
+testSoundsDir("", sfxDir)
+print "Checking music dir"
+if silent != True:
+ testSoundsDir("", musicDir)
+showFooter()
+if errors > 0 or warnings > 0:
+ exit(1)