roc/gamedata.py
2017-05-08 13:10:48 -07:00

276 lines
6.9 KiB
Python
Executable file

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 """<GameDataNode "%s">""" % (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