Package fife :: Package extensions :: Package serializers :: Module xmlobject
[hide private]
[frames] | no frames]

Source Code for Module fife.extensions.serializers.xmlobject

  1  # -*- coding: utf-8 -*- 
  2  # #################################################################### 
  3  #  Copyright (C) 2005-2019 by the FIFE team 
  4  #  http://www.fifengine.net 
  5  #  This file is part of FIFE. 
  6  # 
  7  #  FIFE is free software; you can redistribute it and/or 
  8  #  modify it under the terms of the GNU Lesser General Public 
  9  #  License as published by the Free Software Foundation; either 
 10  #  version 2.1 of the License, or (at your option) any later version. 
 11  # 
 12  #  This library is distributed in the hope that it will be useful, 
 13  #  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 15  #  Lesser General Public License for more details. 
 16  # 
 17  #  You should have received a copy of the GNU Lesser General Public 
 18  #  License along with this library; if not, write to the 
 19  #  Free Software Foundation, Inc., 
 20  #  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA 
 21  # #################################################################### 
 22   
 23  """ submodule for xml map parsing """ 
 24  from __future__ import print_function 
 25   
 26  from builtins import str 
 27  from builtins import object 
 28  from fife import fife 
 29   
 30  from fife.extensions.serializers import ET 
 31  from fife.extensions.serializers import SerializerError, InvalidFormat 
 32  from fife.extensions.serializers import NameClash, NotFound, WrongFileType 
 33  from fife.extensions.serializers.xmlanimation import loadXMLAnimation 
 34   
35 -class XMLObjectSaver(object):
36 """ The B{XMLObjectSaver} serializes a fife.Object instance by saving 37 it back to its XML file 38 39 @note: 40 - this code does NOT allow the creation of a new xml file 41 - this code does NOT touch atlas or animation definitions 42 - this code does NOT allow saving to non-well-formed xml files 43 - this code DOES save blocking & static flag, as well as 44 image offsets 45 46 @type engine: fife 47 @ivar engine: pointer to initialized fife engine instance 48 @type img_manager: fife.ImageManager 49 @ivar img_manager: pointer to fife image manager 50 @type compat: bool 51 @ivar compat: flag to either use outdated xml definitions or new approach 52 @type debug: bool 53 @ivar debug: flag to activate/deactivate debug output 54 @type vfs: fife.VFS 55 @ivar vfs: pointer to fife vfs 56 @type change: bool 57 @ivar change: flag if object data differs from file data 58 """ 59 PROCESSING_INSTRUCTION = '<?fife type="object"?>'
60 - def __init__(self, engine, debug=False, compat=True):
61 """ 62 63 @type engine: fife 64 @param engine: intialized fife engine 65 """ 66 self.compat = compat 67 self.debug = debug 68 self.engine = engine 69 self.img_manager = engine.getImageManager() 70 self.vfs = self.engine.getVFS() 71 self.change = False
72
73 - def save(self, object):
74 """ saves the data of a fife.Object to its xml file 75 76 @type object: fife.Object 77 @param object: the object which should be saved 78 @rtype bool 79 @return flag whether the saving was successful or not 80 """ 81 self.change = False 82 result = False 83 84 file = object.getFilename() 85 if not file: 86 raise SerializerError("Object cannot be saved, no file found %s" % object) 87 return result 88 89 if not self.vfs.exists(file): 90 raise NotFound("File not within vfs: %s" % file) 91 return result 92 93 file_handle = self.vfs.open(file) 94 file_handle.thisown = 1 95 tree = ET.parse(file_handle) 96 root = tree.getroot() 97 98 object_id = object.getId() 99 blocking = object.isBlocking() 100 static = object.isStatic() 101 102 cost_id = object.getCostId() 103 cost = object.getCost() 104 cellstack_pos = object.getCellStackPosition() 105 106 if self.debug: 107 print("XML tree dump: (pre-save)") 108 ET.dump(root) 109 print("Object data: ") 110 print("\tid", object_id) 111 print("\tblocking", blocking) 112 print("\tstatic", static) 113 print("\tcost id", cost_id) 114 print("\tcost", cost) 115 116 # check for compat mode 117 if root.tag != 'assets': 118 self.compat = True 119 120 # in compat mode tree is <object> 121 if self.compat: 122 objects = [root,] 123 # new XML structure has tree root <assets> which groups multiple objects 124 else: 125 objects = root.findall("object") 126 127 for obj in objects: 128 _id = obj.get("id") 129 if _id != object_id: 130 if self.debug: 131 print("...ommitting object %s " % _id) 132 continue 133 134 if int(obj.attrib['blocking']) != int(blocking): 135 self.change = True 136 if int(obj.attrib['static']) != int(static): 137 self.change = True 138 if str(obj.attrib['cost_id']) != str(cost_id): 139 self.change = True 140 if float(obj.attrib['cost']) != float(cost): 141 self.change = True 142 if int(obj.attrib['cellstack_position']) != int(cellstack_pos): 143 self.change = True 144 145 obj.attrib['blocking'] = str(int(blocking)) 146 obj.attrib['static'] = str(int(static)) 147 obj.attrib['cost_id'] = str(cost_id) 148 obj.attrib['cost'] = str(cost) 149 obj.attrib['cellstack_position'] = str(cellstack_pos) 150 151 if self.debug and self.change: 152 print("\tSet new data in xml tree: ") 153 print("\t\tblocking: ", obj.attrib['blocking']) 154 print("\t\tstatic: ", obj.attrib['static']) 155 156 images = obj.findall("image") 157 actions = obj.findall("action") 158 159 if self.debug: 160 print("\tAttempting to save image data: ") 161 print("\t...found these image elements: ") 162 print("\t", images) 163 print("object dump: ") 164 print(ET.dump(obj)) 165 166 self.save_images(images, object) 167 self.save_actions(actions, object) 168 169 if not self.change: 170 return result 171 172 xmlcontent = ET.tostring(root) 173 174 if self.debug: 175 print("XML tree dump: (post-manipulation)") 176 ET.dump(root) 177 178 # save xml data beneath the <?fife type="object"?> definition into the object file 179 file = open(file, 'w') 180 file.write(XMLObjectSaver.PROCESSING_INSTRUCTION+'\n') 181 file.write(xmlcontent + "\n") 182 file.close() 183 result = True 184 return result
185
186 - def save_actions(self, actions, object):
187 """ save action definitions 188 189 @type actions: list 190 @param actions: list of <action> elements 191 @type object: fife.Object 192 @param object: the object which should be saved 193 """ 194 for element in actions: 195 # new xml format uses this, we only save the new format 196 if 'animation_id' not in element.attrib: 197 break 198 199 animation_id = element.attrib['animation_id'] 200 self.save_animation(animation_id, object)
201
202 - def save_animation(self, animation_id, object):
203 """ save animation definitions for the given id 204 205 @type animation_id: str 206 @param animation_id: id of the animation data structure 207 @type object: fife.Object 208 @param object: the object which should be saved 209 """ 210 pass
211
212 - def save_images(self, images, object):
213 """ save image definitions 214 215 @type images: list 216 @param images: list of <image> elements 217 @type object: fife.Object 218 @param object: the object which should be saved 219 """ 220 visual = object.get2dGfxVisual() 221 angles = visual.getStaticImageAngles() 222 if self.debug: 223 print("\t\tobject angles: ", angles) 224 225 for element in images: 226 angle = int(element.get("direction")) 227 if angle not in angles: continue 228 229 index = visual.getStaticImageIndexByAngle(angle) 230 image = self.img_manager.get(index) 231 x_offset = image.getXShift() 232 y_offset = image.getYShift() 233 234 if 'x_offset' not in element.attrib or int(element.attrib['x_offset']) != x_offset: 235 self.change = True 236 if 'y_offset' not in element.attrib or int(element.attrib['y_offset']) != y_offset: 237 self.change = True 238 239 element.attrib['x_offset'] = str(x_offset) 240 element.attrib['y_offset'] = str(y_offset) 241 242 if self.debug and self.change: 243 print("\tSet new data in xml tree: (<image>) ") 244 print("\t\tx offset: ", element.attrib['x_offset']) 245 print("\t\ty offset: ", element.attrib['y_offset'])
246
247 -class XMLObjectLoader(object):
248 """ 249 250 """
251 - def __init__(self, engine):
252 """ 253 254 """ 255 self.engine = engine 256 self.imgMgr = engine.getImageManager() 257 self.anim_pool = None 258 self.model = engine.getModel() 259 self.vfs = engine.getVFS() 260 self.source = None 261 self.filename = ''
262
263 - def loadResource(self, location):
264 """ 265 266 """ 267 self.source = location 268 self.filename = self.source 269 self.node = None 270 self.file = None 271 if hasattr(location, 'node'): 272 self.node = location.node 273 else: 274 isobjectfile = True 275 f = self.vfs.open(self.filename) 276 f.thisown = 1 277 278 obj_identifier = '<?fife type="object"?>' 279 try: 280 s = f.readString(len(obj_identifier)) 281 except fife.IndexOverflow: 282 isobjectfile = False 283 284 if isobjectfile and not s.startswith(obj_identifier): 285 isobjectfile = False 286 287 if not isobjectfile: 288 return 289 290 # this will never be hit currently, if this is put before the return it can provide useful debugging 291 # but animation.xml files will raise this exception because apparently they come through here first 292 raise WrongFileType('Tried to open non-object file %s with XMLObjectLoader.' % self.filename) 293 294 self.do_load_resource(f)
295
296 - def do_load_resource(self, file):
297 """ 298 299 """ 300 if file: 301 tree = ET.parse(file) 302 self.node = tree.getroot() 303 self.parse_object(self.node)
304
305 - def parse_object(self, object):
306 """ 307 308 """ 309 if self.node.tag != 'object': 310 raise InvalidFormat('Expected <object> tag, but found <%s>.' % self.node.tag) 311 312 _id = object.get('id') 313 if not _id: 314 raise InvalidFormat('<object> declared without an id attribute.') 315 _id = str(_id) 316 317 nspace = object.get('namespace') 318 if not nspace: 319 raise InvalidFormat('<object> %s declared without a namespace attribute.' % str(_id)) 320 nspace = str(nspace) 321 322 obj = None 323 parent = object.get('parent', None) 324 if parent: 325 query = self.metamodel.getObjects('id', str(parent)) 326 if len(query) == 0: 327 raise NotFound('No objects found with identifier %s.' % str(parent)) 328 elif len(query) > 1: 329 raise NameClash('%d objects found with identifier %s.' % (len(query), str(parent))) 330 parent = query[0] 331 332 # check if model already has this object 333 if not bool(self.model.getObject(_id, nspace)): 334 obj = self.model.createObject(_id, nspace, parent) 335 else: 336 print(NameClash('Tried to create already existing object \n\t...ignoring: %s, %s' % (_id, nspace))) 337 return 338 339 obj.setFilename(self.source) 340 fife.ObjectVisual.create(obj) 341 obj.setBlocking(bool( int(object.get('blocking', False)) )) 342 obj.setStatic(bool( int(object.get('static', False)) )) 343 344 pather = object.get('pather', 'RoutePather') 345 obj.setPather( self.model.getPather(pather) ) 346 347 self.parse_images(object, obj) 348 self.parse_actions(object, obj)
349
350 - def parse_images(self, objelt, object):
351 """ 352 353 """ 354 for image in objelt.findall('image'): 355 source = image.get('source') 356 if not source: 357 raise InvalidFormat('<image> declared without a source attribute.') 358 359 # paths are relative to this resource's path 360 path = self.filename.split('/') 361 path.pop() 362 path.append(str(source)) 363 364 img = self.imgMgr.create('/'.join(path)) 365 img.setXShift(int( image.get('x_offset', 0) )) 366 img.setYShift(int( image.get('y_offset', 0) )) 367 368 object.get2dGfxVisual().addStaticImage(int( image.get('direction', 0) ), img.getHandle())
369
370 - def parse_actions(self, objelt, object):
371 """ 372 373 """ 374 for action in objelt.findall('action'): 375 id = action.get('id') 376 if not id: 377 raise InvalidFormat('<action> declared without an id attribute.') 378 379 act_obj = object.createAction(str(id)) 380 fife.ActionVisual.create(act_obj) 381 self.parse_animations(action, act_obj)
382
383 - def parse_animations(self, actelt, action):
384 """ 385 386 """ 387 pass 388 for anim in actelt.findall('animation'): 389 source = anim.get('source') 390 if not source: 391 raise InvalidFormat('Animation declared with no source location.') 392 393 # animation paths are relative to this resource's path 394 path = self.filename.split('/') 395 path.pop() 396 path.append(str(source)) 397 398 animation = loadXMLAnimation(self.engine, '/'.join(path)) 399 action.get2dGfxVisual().addAnimation(int( anim.get('direction', 0) ), animation) 400 action.setDuration(animation.getDuration())
401