import glob import os try: from xml.etree.cElementTree import parse except ImportError: from elementtree.ElementTree import parse import files import shader from deprecate import deprecated class MissingNode(object): def __init__(self): self.text = None def get(self, name): return None class GameDataTagDef(object): def __init__(self): self.multi = False self.opt = False self.type = None self.default = None class GameDataNode(object): def __init__(self): self.dict = {} self.missing = False def __setitem__(self, name, val): self.dict[name] = val def __getitem__(self, name): return self.dict[name]._value def __getattr__(self, name): if name == 'dict': raise AttributeError(name) try: return self.dict[name] except KeyError: if name == '_value': return None raise AttributeError(name) def __setattr__(self, name, val): if name == 'dict': return object.__setattr__(self, name, val) try: self.dict[name] = val except KeyError: object.__setattr__(self, name, val) def add_multi(self, key, value): if key not in self.dict: self.dict[key] = [] self.dict[key].append(value) def add_single(self, key, value): self.dict[key] = value def __repr__(self): return """""" % (self.dict['_name'],) def has_key(self, key): return key in self.dict class XMLGameDataReader(object): def __init__(self, bin, xml): self._bin = bin self._xml = xml self._tree = self.construct() self._name = xml.tag def value_as_type(self, value, type): if type == 'string': return value elif type == 'bool': if value and value[0].lower() in ('y', 't', '1'): return True else: return False elif type == 'int': return int(value) elif type == 'float': return float(value) elif type == 'csvarray': # csvarrays are always ints... return [int(x.strip()) for x in value.split(',')] else: raise TypeError(type) def create_datatag_def(self, tag): d = GameDataTagDef() if tag.get('multiple') != None and tag.get('multiple')[0].lower() == 'y': d.multi = True if tag.get('optional') != None and tag.get('optional')[0].lower() == 'y': d.opt = True if tag.get('data') != None: d.type = tag.get('data') if tag.get('default') != None: d.default = self.value_as_type(tag.get('default'), d.type) d.opt = True return d def create_attribute_def(self, tag): return self.create_datatag_def(tag) def construct_node(self, bin, xml, allow_missing=False): node = GameDataNode() if isinstance(xml, MissingNode): node.missing = True value = None if bin.tag == 'datatag': df = self.create_datatag_def(bin) if df.type: value = xml.text if df.type == 'bool': # if tag exists, since it is a bool, that means it's True value = not node.missing elif (node.missing or value == None) and df.opt: value = df.default elif (node.missing or value == None) and not allow_missing: raise ValueError("Missing value for mandatory tag %s" % (bin.get('name'),)) elif (node.missing or value == None): value = None else: value = self.value_as_type(value, df.type) node['_value'] = value elif bin.tag == 'attribute': df = self.create_attribute_def(bin) if df.type: value = xml.get(bin.get('name')) if value == None and df.opt: value = df.default elif not value and not allow_missing: raise ValueError("Missing value for mandatory tag %s" % (bin.get('name'),)) elif not value: value = None else: value = self.value_as_type(value, df.type) node['_value'] = value node['_def'] = df node['_name'] = bin.get('name') return node def construct_recurse(self, bin, xml, allow_missing=False): xmldict = {} tagdict = {} attrdict = {} if bin.tag == 'xml_binary_packing': node = GameDataNode() else: node = self.construct_node(bin, xml, allow_missing) for child in bin.getchildren(): if child.tag == 'datatag': tagdict[child.get('name')] = child elif child.tag == 'attribute': attrdict[child.get('name')] = child else: raise ValueError xmlattrdict = {} xmltagdict = {} if not isinstance(xml, MissingNode): for k, v in list(xml.items()): if not k in attrdict: raise ValueError("Key %s not a valid attribute: %s" % (k, list(attrdict.keys()))) continue binchild = attrdict[k] xmlchild = xml xmlattrdict[k] = 0 subnode = self.construct_node(binchild, xml, allow_missing) if subnode._def.multi: node.add_multi(k, subnode) else: node.add_single(k, subnode) for child in xml.getchildren(): if not child.tag in tagdict: raise ValueError continue binchild = tagdict[child.tag] xmlchild = child xmltagdict[child.tag] = 0 subnode = self.construct_recurse(binchild, xmlchild, allow_missing) if subnode._def.multi: node.add_multi(child.tag, subnode) else: node.add_single(child.tag, subnode) missing = MissingNode() for k in list(tagdict.keys()): if not k in xmltagdict: # Missing datatag subnode = self.construct_recurse(tagdict[k], missing, isinstance(xml, MissingNode)) if not subnode._def.multi: node.add_single(k, subnode) else: node.add_single(k, []) for k in list(attrdict.keys()): if not k in xmlattrdict: # Missing attribute subnode = self.construct_node(attrdict[k], missing, isinstance(xml, MissingNode)) if not subnode._def.multi: node.add_single(k, subnode) else: node.add_single(k, []) return node def construct(self): rootname = self._bin.get('name') assert rootname == self._xml.tag return self.construct_recurse(self._bin, self._xml) def __getattr__(self, name): if name == 'tree': raise AttributeError(name) return self._tree.__getattr__(name) def __getitem__(self, name): return self._tree.__getitem__(name) def load_xml_files(binfile, xmlfile): bintree = parse(binfile).getroot() xmltree = parse(xmlfile).getroot() return XMLGameDataReader(bintree, xmltree) def get(dataname): if dataname in get.cache: return get.cache[dataname] bin = files.mgr.path("xml/def", "%s.xml" % (dataname,)) xml = files.mgr.path("xml", "%s.xml" % (dataname,)) if not os.path.exists(bin) or not os.path.exists(xml): raise OSError("XML data file does not exist") dataobj = load_xml_files(bin, xml) get.cache[dataname] = dataobj return dataobj get.cache = {} @deprecated def load_gamedata(): bins = glob.glob(files.mgr.path("xml/def", "*.xml")) xmls = glob.glob(files.mgr.path("xml", "*.xml")) binfns = [os.path.split(x)[1] for x in bins] xmlfns = [os.path.split(x)[1] for x in xmls] ffns = [] for bfn in binfns: if bfn in xmlfns: ffns.append(bfn) dataobjs = {} for fn in ffns: binfile = files.mgr.path("xml/def", fn) xmlfile = files.mgr.path("xml", fn) dataobj = load_xml_files(binfile, xmlfile) dataobjs[dataobj._name] = dataobj shader.mgr.load_shaders(dataobjs['shaders']) return dataobjs