changeset 5:03fe34478f69

Using tiled for maps
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Sun, 17 Jun 2012 16:34:43 +0200
parents 2661d2091f1d
children 96fffc932ba7
files 000/005.py 002/000.py 002/001.py 002/002.py 002/003.py 002/control.png 002/level.tmx 002/pytmx/__init__.py 002/pytmx/constants.py 002/pytmx/pytmx.py 002/pytmx/tmxloader.py 002/pytmx/tmxloader3.py 002/pytmx/utils.py 002/pytmx_docs/README 002/pytmx_docs/lgpl-3.0.txt 002/pytmx_docs/tests/16x16-overworld.png 002/pytmx_docs/tests/16x16-overworld.tsx 002/pytmx_docs/tests/demo.py 002/pytmx_docs/tests/desert.tmx 002/pytmx_docs/tests/formosa-base64-gzip.tmx 002/pytmx_docs/tests/formosa-base64-zlib.tmx 002/pytmx_docs/tests/formosa-base64.tmx 002/pytmx_docs/tests/formosa-csv.tmx 002/pytmx_docs/tests/formosa-xml.tmx 002/pytmx_docs/tests/frnknstn.tmx 002/pytmx_docs/tests/pytmx 002/pytmx_docs/tests/sewer_tileset.png 002/pytmx_docs/tests/sewers.tmx 002/pytmx_docs/tests/test.py 002/pytmx_docs/tests/tiles.png 002/pytmx_docs/tests/tmw_desert_spacing.png 002/spritesheet.png 002/tilesheet.png
diffstat 33 files changed, 3710 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/000/005.py	Sun Jun 17 00:18:38 2012 +0200
+++ b/000/005.py	Sun Jun 17 16:34:43 2012 +0200
@@ -26,13 +26,11 @@
 
 def wait_for_quit():
     """Sit there doing nothing until the window is closed."""
-    clock = pygame.time.Clock()
     pygame.display.update()
     while True:
         event = pygame.event.wait()
         if event.type == pg.QUIT:
             return
-        clock.tick(60)
 
 
 if __name__ == '__main__':
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/000.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import pygame
+import pygame.locals as pg
+
+
+def load_sheet(filename, width=32, height=None):
+    if height is None:
+        height = width
+    image = pygame.image.load(filename)
+    image = image.convert()
+    image.set_colorkey((0xff, 0, 0xff))
+    w, h = image.get_size()
+    images = []
+    for x in xrange(w // width):
+        for y in xrange(h // height):
+            rect = pygame.rect.Rect(x * width, y * height, width, height)
+            images.append(image.subsurface(rect))
+    return images
+
+
+class Game(object):
+    def __init__(self):
+        pygame.display.init()
+        pygame.font.init()
+        self.screen = pygame.display.set_mode((800, 600))
+        self.screen.fill((0x66, 0x66, 0x66))
+        self.tilesheet = load_sheet('tilesheet.png')
+        self.font = pygame.font.Font(None, 16)
+
+    def display(self):
+        for i, tile in enumerate(self.tilesheet):
+            x, y = divmod(i, 600 // 33)
+            self.screen.blit(tile, (x * 33 + 1, y * 33 + 1))
+            label = self.font.render('%d' % i, False, (0xff, 0xff, 0xff))
+            back = self.font.render('%d' % i, False, (0x00, 0x00, 0x00))
+            self.screen.blit(back, (x * 33 + 1 - 1, y * 33 + 1))
+            self.screen.blit(back, (x * 33 + 1 + 1, y * 33 + 1))
+            self.screen.blit(back, (x * 33 + 1, y * 33 + 1 - 1))
+            self.screen.blit(back, (x * 33 + 1, y * 33 + 1 + 1))
+            self.screen.blit(label, (x * 33 + 1, y * 33 + 1))
+        pygame.display.update()
+
+    def control(self):
+        event = pygame.event.wait()
+        if event.type == pg.QUIT:
+            self.game_over = True
+
+    def run(self):
+        self.game_over = False
+        while not self.game_over:
+            self.display()
+            self.control()
+
+if __name__ == '__main__':
+    game = Game()
+    game.run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/001.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import pygame
+import pygame.locals as pg
+
+LAYERS = [
+    [
+        'XXXXXXXXXXXXXXX',
+        'X.............X',
+        'X.............X',
+        'X...XXXXXXX...X',
+        'X...XXXXXXX...X',
+        'X...XXXXXXX...X',
+        'X.............X',
+        'X.............X',
+        'XXXXXXXXXXXXXXX',
+    ],
+]
+
+TILES = {
+    '.': 27,
+    'X': 11,
+}
+
+
+def load_sheet(filename, width=32, height=None):
+    if height is None:
+        height = width
+    image = pygame.image.load(filename)
+    image = image.convert()
+    image.set_colorkey((0xff, 0, 0xff))
+    w, h = image.get_size()
+    images = []
+    for x in xrange(w // width):
+        for y in xrange(h // height):
+            rect = pygame.rect.Rect(x * width, y * height, width, height)
+            images.append(image.subsurface(rect))
+    return images
+
+
+class Level(object):
+    def __init__(self, layers, tiles):
+        self.layers = []
+        for layer in layers:
+            self.layers.append([[tiles[c] for c in row] for row in layer])
+
+    def render(self, screen, tilesheet):
+        for layer in self.layers:
+            for y, row in enumerate(layer):
+                for x, tile in enumerate(row):
+                    screen.blit(tilesheet[tile], (x * 32, y * 32))
+
+
+class Game(object):
+    def __init__(self):
+        pygame.display.init()
+        pygame.font.init()
+        self.screen = pygame.display.set_mode((800, 600))
+        self.screen.fill((0x66, 0x66, 0x66))
+        self.tilesheet = load_sheet('tilesheet.png')
+        self.font = pygame.font.Font(None, 16)
+
+    def load_level(self, layers, tiles):
+        self.level = Level(layers, tiles)
+
+    def display(self):
+        self.level.render(self.screen, self.tilesheet)
+        pygame.display.update()
+
+    def control(self):
+        event = pygame.event.wait()
+        if event.type == pg.QUIT:
+            self.game_over = True
+
+    def run(self):
+        self.game_over = False
+        while not self.game_over:
+            self.display()
+            self.control()
+
+
+if __name__ == '__main__':
+    game = Game()
+    game.load_level(LAYERS, TILES)
+    game.run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/002.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import pygame
+import pygame.locals as pg
+
+LAYERS = [
+    [
+        'XxXxXXxxXxXxXxx',
+        'x.............X',
+        'x.............x',
+        'x...XxXxXxX...x',
+        'X...XxxXXxX...X',
+        'X...XxXXxXx...x',
+        'x.............X',
+        'x.............X',
+        'XXxxxXXxXxXXxXx',
+    ],
+    [
+        '               ',
+        ' FMMMMMMMMMMMT ',
+        ' E dWWWWWWWb K ',
+        ' E K       E K ',
+        ' E K       E K ',
+        ' E K       E K ',
+        ' E YMMMMMMMP K ',
+        ' LWWWWWWWWWWWJ ',
+        '               ',
+    ],
+]
+
+TILES = {
+    ' ': -1, '.': 27, 'X': 11, 'd': 2, 'K': 3, 'Y': 4, 'b': 14, 'E': 15,
+    'P': 16, 'M': 10, 'W': 8, 'L': 7, 'F': 6, 'J': 13, 'T': 12, 'x': 9,
+}
+
+
+def load_sheet(filename, width=32, height=None):
+    if height is None:
+        height = width
+    image = pygame.image.load(filename)
+    image = image.convert()
+    image.set_colorkey((0xff, 0, 0xff))
+    w, h = image.get_size()
+    images = []
+    for x in xrange(w // width):
+        for y in xrange(h // height):
+            rect = pygame.rect.Rect(x * width, y * height, width, height)
+            images.append(image.subsurface(rect))
+    return images
+
+
+class Level(object):
+    def __init__(self, layers, tiles):
+        self.layers = []
+        for layer in layers:
+            self.layers.append([[tiles[c] for c in row] for row in layer])
+
+    def render(self, screen, tilesheet):
+        for layer in self.layers:
+            for y, row in enumerate(layer):
+                for x, tile in enumerate(row):
+                    if tile >= 0:
+                        screen.blit(tilesheet[tile], (x * 32, y * 32))
+
+
+class Game(object):
+    def __init__(self):
+        pygame.display.init()
+        pygame.font.init()
+        self.screen = pygame.display.set_mode((800, 600))
+        self.screen.fill((0x66, 0x66, 0x66))
+        self.tilesheet = load_sheet('tilesheet.png')
+        self.font = pygame.font.Font(None, 16)
+
+    def load_level(self, layers, tiles):
+        self.level = Level(layers, tiles)
+
+    def display(self):
+        self.level.render(self.screen, self.tilesheet)
+        pygame.display.update()
+
+    def control(self):
+        event = pygame.event.wait()
+        if event.type == pg.QUIT:
+            self.game_over = True
+
+    def run(self):
+        self.game_over = False
+        while not self.game_over:
+            self.display()
+            self.control()
+
+
+if __name__ == '__main__':
+    game = Game()
+    game.load_level(LAYERS, TILES)
+    game.run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/003.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import pygame
+import pygame.locals as pg
+from pytmx import tmxloader
+
+
+class Level(object):
+    def __init__(self, filename):
+        self.tiled_map = tmxloader.load_pygame(filename)
+
+    def render(self, screen):
+        w = self.tiled_map.tilewidth
+        h = self.tiled_map.tileheight
+        get_image = self.tiled_map.getTileImage
+        for l, layer in enumerate(self.tiled_map.tilelayers):
+            for y in xrange(self.tiled_map.height):
+                for x in xrange(self.tiled_map.width):
+                    image = get_image(x, y, l)
+                    if image:
+                        screen.blit(image, (x * w, y * h))
+
+
+class Game(object):
+    def __init__(self):
+        pygame.display.init()
+        pygame.font.init()
+        self.screen = pygame.display.set_mode((800, 600))
+        self.screen.fill((0x66, 0x66, 0x66))
+
+    def load_level(self, filename):
+        self.level = Level(filename)
+
+    def display(self):
+        self.level.render(self.screen)
+        pygame.display.update()
+
+    def control(self):
+        event = pygame.event.wait()
+        if event.type == pg.QUIT:
+            self.game_over = True
+
+    def run(self):
+        self.game_over = False
+        while not self.game_over:
+            self.display()
+            self.control()
+
+
+if __name__ == '__main__':
+    game = Game()
+    game.load_level('level.tmx')
+    game.run()
Binary file 002/control.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/level.tmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="25" height="18" tilewidth="32" tileheight="32">
+ <tileset firstgid="1" name="control" tilewidth="32" tileheight="32">
+  <properties>
+   <property name="block" value="1"/>
+  </properties>
+  <image source="control.png" trans="ff00ff" width="64" height="32"/>
+  <tile id="0">
+   <properties>
+    <property name="block" value="1"/>
+   </properties>
+  </tile>
+ </tileset>
+ <tileset firstgid="3" name="tilesheet" tilewidth="32" tileheight="32">
+  <image source="tilesheet.png" trans="ff00ff" width="288" height="192"/>
+ </tileset>
+ <layer name="Control" width="25" height="18">
+  <data encoding="base64" compression="zlib">
+   eJxjZGBgYKQDpiUY7nYwUYBH7Ri1Y9SOoWEHNcFws4PWGABJRwF/
+  </data>
+ </layer>
+ <layer name="Background" width="25" height="18">
+  <data encoding="base64" compression="zlib">
+   eJyTZ2BgkKcDVqIhHu52UDPcR6Id+MJ61A762zFc0hW17aBHWTJU7aA1BgDmfzlJ
+  </data>
+ </layer>
+ <layer name="Corners" width="25" height="18">
+  <data encoding="base64" compression="zlib">
+   eJy1VdtqwzAMFWQbFHaBwNq1DyFhD92lD7Zr//+v1WY65ERTRyFMIOzKro6OJCtJRJL8SNA1qi3KLNG5x8L/xd1E+7uqH/+g94Q7OnF1tM/mrNBanDt91XPVQeZcjHT/tep2he4Ia9A1EI9i7EJ7rk3642yk/UB+rN1KvNEGX4+Or2YveoYcIta2f6u6d/RAPsDnVh5BMXA+mVizxvRu4mFfmeJFPc4OP+RjonPwFcXge168ydiBLbJ8g5P63hAOYzw4voABns3ey9xXQZb8OVdFOWXFsPMAGB3xQJ3whry++qr67ehJzzE/PB4Wm+0v9H87n7jO3BuoLXLqYXTyu+acMwj7tdjwhXeAvDOPo6ybg5+y7FGuB3qlSSa+ntgZCWk+evrN7+NJ1s3Ba/qsWODC3xbuRc47vjtB5tlh5yMk0noBeX8qEg==
+  </data>
+ </layer>
+</map>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx/__init__.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,5 @@
+from tmxloader import load_tmx, load_pygame
+from pytmx import TiledElement, TiledMap, TiledTileset, TiledLayer, \
+                  TiledObjectGroup, TiledObject
+from utils import buildDistributionRects
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx/constants.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,11 @@
+# internal flags
+TRANS_FLIPX = 1
+TRANS_FLIPY = 2
+TRANS_ROT = 4
+
+
+# Tiled gid flags
+GID_TRANS_FLIPX = 1<<31
+GID_TRANS_FLIPY = 1<<30
+GID_TRANS_ROT   = 1<<29
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx/pytmx.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,572 @@
+from itertools import chain, product
+from xml.etree import ElementTree
+from collections import defaultdict 
+from utils import decode_gid, types, parse_properties, read_points
+from constants import *
+
+
+
+"""
+Class declarations and other important stuff
+"""
+
+
+
+class TiledElement(object):
+    def set_properties(self, node):
+        """
+        read the xml attributes and tiled "properties" from a xml node and fill
+        in the values into the object's dictionary.  Names will be checked to
+        make sure that they do not conflict with reserved names.
+        """
+
+        # set the attributes reserved for tiled
+        [ setattr(self, k, types[str(k)](v)) for (k,v) in node.items() ] 
+
+        # set the attributes that are derived from tiled 'properties'
+        for k,v in parse_properties(node).items():
+            if k in self.reserved:
+                msg = "{} \"{}\" has a property called \"{}\""
+                print msg.format(self.__class__.__name__, self.name, k, self.__class__.__name__)
+                msg = "This name is reserved for {} objects and can cannot be used."
+                print msg.format(self.__class__.__name__)
+                print "Please change the name in Tiled and try again."
+                print v
+                raise ValueError
+            setattr(self, k, types[str(k)](v))
+
+
+class TiledMap(TiledElement):
+    """
+    Contains the tile layers, tile images, object groups, and objects from a
+    Tiled TMX map.
+    """
+
+    reserved = "version orientation width height tilewidth tileheight properties tileset layer objectgroup".split()
+
+
+    def __init__(self, filename):
+        from collections import defaultdict
+
+        TiledElement.__init__(self)
+        self.tilesets = []          # list of TiledTileset objects
+        self.tilelayers   = []      # list of TiledLayer objects
+        self.objectgroups = []      # list of TiledObjectGroup objects
+        self.tile_properties = {}   # dict of tiles that have metadata
+        self.filename = filename
+
+        # only used tiles are actually loaded, so there will be a difference
+        # between the GID's in the Tile map data (tmx) and the data in this
+        # class and the layers.  This dictionary keeps track of that difference.
+        self.gidmap = defaultdict(list)
+
+        # should be filled in by a loader function
+        self.images = []
+
+        # defaults from the TMX specification
+        self.version = 0.0
+        self.orientation = None
+        self.width = 0
+        self.height = 0
+        self.tilewidth = 0
+        self.tileheight = 0 
+
+        self.imagemap = {}  # mapping of gid and trans flags to real gids
+        self.maxgid = 1
+
+        self.load()
+
+
+    def __repr__(self):
+        return "<{}: \"{}\">".format(self.__class__.__name__, self.filename)
+
+
+    def getTileImage(self, x, y, layer):
+        """
+        return the tile image for this location
+        x and y must be integers and are in tile coordinates, not pixel
+
+        return value will be 0 if there is no tile with that location.
+        """
+
+        try:
+            gid = self.tilelayers[int(layer)].data[int(y)][int(x)]
+        except (IndexError, ValueError):
+            msg = "Coords: ({0},{1}) in layer {2} is not valid."
+            raise Exception, msg.format(x, y, layer)
+        except TypeError:
+            msg = "Tiles must be specified in integers."
+            raise TypeError, msg
+
+        else:
+            try:
+                return self.images[gid]
+            except (IndexError, ValueError):
+                msg = "Coords: ({0},{1}) in layer {2} has invalid GID: {3}"
+                raise Exception, msg.format(x, y, layer, gid)
+
+
+    def getTileGID(self, x, y, layer):
+        """
+        return GID of a tile in this location
+        x and y must be integers and are in tile coordinates, not pixel
+        """
+
+        try:
+            return self.tilelayers[int(layer)].data[int(y)][int(x)]
+        except (IndexError, ValueError):
+            msg = "Coords: ({0},{1}) in layer {2} is invalid"
+            raise Exception, msg.format(x, y, layer)
+
+
+    def getDrawOrder(self):
+        """
+        return a list of objects in the order that they should be drawn
+        this will also exclude any layers that are not set to visible
+
+        may be useful if you have objects and want to control rendering
+        from tiled
+        """
+
+        raise NotImplementedError
+
+
+    def getTileImages(self, r, layer):
+        """
+        return a group of tiles in an area
+        expects a pygame rect or rect-like list/tuple
+
+        useful if you don't want to repeatedly call getTileImage
+        """
+
+        raise NotImplementedError
+
+
+    def getObjects(self):
+        """
+        Return iterator of all the objects associated with this map
+        """
+
+        return chain(*(i for i in self.objectgroups))
+
+
+    def getTileProperties(self, (x, y, layer)):
+        """
+        return the properties for the tile, if any
+        x and y must be integers and are in tile coordinates, not pixel
+
+        returns a dict of there are properties, otherwise will be None
+        """
+
+        try:
+            gid = self.tilelayers[int(layer)].data[int(y)][int(x)]
+        except (IndexError, ValueError):
+            msg = "Coords: ({0},{1}) in layer {2} is invalid."
+            raise Exception, msg.format(x, y, layer)
+
+        else:
+            try:
+                return self.tile_properties[gid]
+            except (IndexError, ValueError):
+                msg = "Coords: ({0},{1}) in layer {2} has invaid GID: {3}"
+                raise Exception, msg.format(x, y, layer, gid)
+            except KeyError:
+                return None
+
+
+    def getLayerData(self, layer):
+        """
+        Return the data for a layer.
+
+        Data is an array of arrays.
+
+        >>> pos = data[y][x]
+        """
+
+        try:
+            return self.tilelayers[layer].data
+        except IndexError:
+            msg = "Layer {} does not exist."
+            raise ValueError, msg.format(layer)
+
+
+    def getTileLocation(self, gid):
+        # experimental way to find locations of a tile by the GID
+
+        p = product(xrange(self.width),
+                    xrange(self.height),
+                    xrange(len(self.tilelayers)))
+
+        return [ (x,y,l) for (x,y,l) in p 
+               if self.tilelayers[l].data[y][x] == gid ]
+
+
+    def getTilePropertiesByGID(self, gid):
+        try:
+            return self.tile_properties[gid]
+        except KeyError:
+            return None
+
+
+    def setTileProperties(self, gid, d):
+        """
+        set the properties of a tile by GID.
+        must use a standard python dict as d
+        """
+
+        try:
+            self.tile_properties[gid] = d
+        except KeyError:
+            msg = "GID #{} does not exist."
+            raise ValueError, msg.format(gid)
+
+
+    def getTilePropertiesByLayer(self, layer):
+        """
+        Return a list of tile properties (dict) in use in this tile layer.
+        """
+
+        try:
+            layer = int(layer)
+        except:
+            msg = "Layer must be an integer.  Got {} instead."
+            raise ValueError, msg.format(type(layer))
+
+        p = product(range(self.width),range(self.height))
+        layergids = set( self.tilelayers[layer].data[y][x] for x, y in p )
+
+        props = []
+        for gid in layergids:
+            try:
+                props.append((gid, self.tile_properties[gid]))
+            except:
+                continue
+            
+        return props
+
+
+    def registerGID(self, real_gid, flags=0):
+        """
+        used to manage the mapping of GID between the tmx data and the internal
+        data.
+        
+        number returned is gid used internally
+        """
+
+        if real_gid:
+            try:
+                return self.imagemap[(real_gid, flags)][0]
+            except KeyError:
+                # this tile has not been encountered before, or it has been
+                # transformed in some way.  make a new GID for it.
+                gid = self.maxgid
+                self.maxgid += 1
+                self.imagemap[(real_gid, flags)] = (gid, flags)
+                self.gidmap[real_gid].append((gid, flags))
+                return gid
+
+        else:
+            return 0
+
+
+
+    def mapGID(self, real_gid):
+        """
+        used to lookup a GID read from a TMX file's data
+        """
+
+        try:
+            return self.gidmap[int(real_gid)]
+        except KeyError:
+            return None
+        except TypeError:
+            msg = "GID's must be an integer"
+            raise TypeError, msg
+
+
+    def loadTileImages(self, filename):
+        raise NotImplementedError
+
+
+    def load(self):
+        """
+        parse a map node from a tiled tmx file
+        """
+        etree = ElementTree.parse(self.filename).getroot()
+        self.set_properties(etree)
+
+        # initialize the gid mapping
+        self.imagemap[(0,0)] = 0
+
+        for node in etree.findall('layer'):
+            self.tilelayers.append(TiledLayer(self, node))
+
+        for node in etree.findall('objectgroup'):
+            self.objectgroups.append(TiledObjectGroup(self, node))
+
+        for node in etree.findall('tileset'):
+            self.tilesets.append(TiledTileset(self, node))
+
+        # "tile objects", objects with a GID, have need to have their
+        # attributes set after the tileset is loaded
+        for o in self.getObjects():
+            p = self.getTilePropertiesByGID(o.gid)
+            if p:
+                o.name = "TileObject"
+                o.__dict__.update(p)
+
+
+    @property
+    def visibleTileLayers(self):
+        return [layer for layer in self.tilelayers if layer.visible]
+
+
+class TiledTileset(TiledElement):
+    reserved = "firstgid source name tilewidth tileheight spacing margin image tile properties".split()
+
+    def __init__(self, parent, node):
+        TiledElement.__init__(self)
+        self.parent = parent
+
+        # defaults from the specification
+        self.firstgid = 0
+        self.source = None
+        self.name = None
+        self.tilewidth = 0
+        self.tileheight = 0
+        self.spacing = 0
+        self.margin = 0
+        self.tiles = {}
+
+        self.parse(node)
+
+    def __repr__(self):
+        return "<{}: \"{}\">".format(self.__class__.__name__, self.name)
+
+
+    def parse(self, node):
+        """
+        parse a tileset element and return a tileset object and properties for
+        tiles as a dict
+
+        a bit of mangling is doew here so that tilesets that have external
+        TSX files appear the same as those that don't
+        """
+        import os
+
+
+        # if true, then node references an external tileset
+        source = node.get('source', False)
+        if source:
+            if source[-4:].lower() == ".tsx":
+
+                # external tilesets don't save this, store it for later
+                self.firstgid = int(node.get('firstgid'))
+
+                # we need to mangle the path - tiled stores relative paths
+                dirname = os.path.dirname(self.parent.filename)
+                path = os.path.abspath(os.path.join(dirname, source))
+                try:
+                    node = ElementTree.parse(path).getroot()
+                except IOError:
+                    msg = "Cannot load external tileset: {}"
+                    raise Exception, msg.format(path)
+
+            else:
+                msg = "Found external tileset, but cannot handle type: {}"
+                raise Exception, msg.format(self.source)
+           
+        self.set_properties(node)
+
+        # since tile objects [probably] don't have a lot of metadata,
+        # we store it seperately in the parent (a TiledMap instance)
+        for child in node.iter('tile'):
+            real_gid = int(child.get("id"))
+            p = parse_properties(child)
+            p['width'] = self.tilewidth
+            p['height'] = self.tileheight
+            for gid, flags in self.parent.mapGID(real_gid + self.firstgid):
+                self.parent.setTileProperties(gid, p)
+
+
+        image_node = node.find('image')
+        self.source = image_node.get('source')
+        self.trans = image_node.get("trans", None)
+
+
+class TiledLayer(TiledElement):
+    reserved = "name x y width height opacity properties data".split()
+
+    def __init__(self, parent, node):
+        TiledElement.__init__(self)
+        self.parent = parent
+        self.data = []
+
+        # defaults from the specification
+        self.name = None
+        self.opacity = 1.0
+        self.visible = True
+       
+        self.parse(node)
+
+
+    def __repr__(self):
+        return "<{}: \"{}\">".format(self.__class__.__name__, self.name)
+
+
+    def parse(self, node):
+        """
+        parse a layer element
+        """
+        from utils import group
+        from itertools import product, imap
+        from struct import unpack
+        import array
+
+        self.set_properties(node)
+
+        data = None
+        next_gid = None
+
+        data_node = node.find('data')
+
+        encoding = data_node.get("encoding", None)
+        if encoding == "base64":
+            from base64 import decodestring
+            data = decodestring(data_node.text.strip())
+
+        elif encoding == "csv":
+            next_gid = imap(int, "".join(
+                line.strip() for line in data_node.text.strip()
+                ).split(","))
+
+        elif encoding:
+            msg = "TMX encoding type: {} is not supported."
+            raise Exception, msg.format(encoding)
+
+        compression = data_node.get("compression", None)
+        if compression == "gzip":
+            from StringIO import StringIO
+            import gzip
+            with gzip.GzipFile(fileobj=StringIO(data)) as fh:
+                data = fh.read()
+
+        elif compression == "zlib":
+            import zlib
+            data = zlib.decompress(data)
+
+        elif compression:
+            msg = "TMX compression type: {} is not supported."
+            raise Exception, msg.format(str(attr["compression"]))
+     
+        # if data is None, then it was not decoded or decompressed, so
+        # we assume here that it is going to be a bunch of tile elements
+        # TODO: this will probably raise an exception if there are no tiles
+        if encoding == next_gid == None:
+            def get_children(parent):
+                for child in parent.findall('tile'):
+                    yield int(child.get('gid'))
+
+            next_gid = get_children(data_node)
+
+        elif data:
+            # data is a list of gid's. cast as 32-bit ints to format properly
+            # create iterator to effeciently parse data
+            next_gid=imap(lambda i:unpack("<L", "".join(i))[0], group(data, 4))
+
+        # using bytes here limits the layer to 256 unique tiles
+        # may be a limitation for very detailed maps, but most maps are not
+        # so detailed. 
+        [ self.data.append(array.array("B")) for i in xrange(self.height) ]
+
+        for (y, x) in product(xrange(self.height), xrange(self.width)):
+            self.data[y].append(self.parent.registerGID(*decode_gid(next(next_gid))))
+
+
+class TiledObjectGroup(TiledElement, list):
+    """
+    Stores TiledObjects.  Supports any operation of a normal list.
+    """
+    reserved = "name color x y width height opacity object properties".split()
+
+    def __init__(self, parent, node):
+        TiledElement.__init__(self)
+        self.parent = parent
+
+        # defaults from the specification
+        self.name = None
+
+        self.parse(node)
+
+
+    def __repr__(self):
+        return "<{}: \"{}\">".format(self.__class__.__name__, self.name)
+
+
+    def parse(self, node):
+        """
+        parse a objectgroup element and return a object group
+        """
+
+        self.set_properties(node)
+
+        for child in node.findall('object'):
+            o = TiledObject(self.parent, child)
+            self.append(o)
+
+
+class TiledObject(TiledElement):
+    reserved = "name type x y width height gid properties polygon polyline image".split()
+
+    def __init__(self, parent, node):
+        TiledElement.__init__(self)
+        self.parent = parent
+
+        # defaults from the specification
+        self.name = None
+        self.type = None
+        self.x = 0
+        self.y = 0
+        self.width = 0
+        self.height = 0
+        self.gid = 0
+
+        self.parse(node)
+
+
+    def __repr__(self):
+        return "<{}: \"{}\">".format(self.__class__.__name__, self.name)
+
+
+    def parse(self, node):
+        self.set_properties(node)
+
+        # correctly handle "tile objects" (object with gid set)
+        if self.gid:
+            self.gid = self.parent.registerGID(self.gid)
+
+        polygon = node.find('polygon')
+        if polygon is not None:
+            x1 = x2 = y1 = y2 = 0
+            self.points = read_points(polygon.get('points'))
+            self.closed = 1
+            for x, y in self.points:
+                if x < x1: x1 = x
+                if x > x2: x2 = x
+                if y < y1: y1 = y
+                if y > y2: y2 = y
+            self.width = abs(x1) + abs(x2) 
+            self.height = abs(y1) + abs(y2)
+
+        polyline = node.find('polyline')
+        if polyline is not None:
+            x1 = x2 = y1 = y2 = 0
+            self.points = read_points(polyline.get('points'))
+            self.closed = 0
+            for x, y in self.points:
+                if x < x1: x1 = x
+                if x > x2: x2 = x
+                if y < y1: y1 = y
+                if y > y2: y2 = y
+            self.width = abs(x1) + abs(x2) 
+            self.height = abs(y1) + abs(y2)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx/tmxloader.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,304 @@
+"""
+Map loader for TMX Files
+bitcraft (leif dot theden at gmail.com)
+v.15 - for python 2.7
+
+If you have any problems or suggestions, please contact me via email.
+Tested with Tiled 0.8.1 for Mac.
+
+released under the LGPL v3
+
+===============================================================================
+
+This map loader can be used to load maps created in the Tiled map editor.  It
+provides a simple way to get tiles and associated metadata so that you can draw
+a map onto the screen.  It does not force you to draw your map in any
+particular way.
+
+This is *not* a rendering engine.  It will load the data that is necessary to
+render a map onto the screen.  All tiles will be loaded into in memory and
+available to blit onto the screen.
+
+
+Design Goals:
+    Simple api
+    Memory efficient and fast
+
+Features:
+    Loads data and "properties" metadata from Tile's TMX format
+    "Properties" for: maps, tilesets, layers, objectgroups, objects, and tiles
+    Point data for polygon and polyline objects
+    Automatic flipping and rotation of tiles
+    Supports base64, csv, gzip, zlib and uncompressed XML
+    Image loading with pygame
+
+New in .15:
+    loader: fixed issue where objects with tile gid did not load properties
+    loader: polygon and polyline objects
+    loader: new lookup methods use iterators
+    loader: loading function moved into classes
+    loader: data/images can be reloaded on the fly
+    loader: uses etree for faster xml parsing
+
+New in .14:
+    loader: Fixed gid lookup for "buildDistributionRects"
+    loader: Added useful output to a few classes "__repr__"
+    loader: Fixed a gid mapping issue that broke rotated tiles
+    pygame: fixed colorkey handling
+    pygame: correctly handles margins and spacing between tiles in tilesets
+    pygame: b/c of changes, now correctly renders tiled's example maps
+    added scrolling demo
+
+New in .13:
+    loader: Renamed "get_tile_image" to "getTileImage"
+    loader: Removed duplicates returned from getTilePropertiesByLayer
+    loader: Modified confusing messages for GID errors
+    loader: Fixed bug where transformed tile properties are not available
+    loader: No longer loads metadata for tiles that are not used
+    loader: Reduced tile cache to 256 unique tiles
+    loader: Removed 'visible' from list of reserved words
+    loader: Added 'buildDistributionRects' and maputils module
+    loader: Added some misc. functions for retrieving properties
+    pygame: Smarter tile management made tile loading cache useless; removed it
+    pygame: pygame.RLEACCEL flag added when appropriate
+
+New in .12:
+    loader: Fixed bug where tile properties could contain reserved words
+    loader: Reduced size of image index by only allocating space for used tiles
+
+New in .11:
+    loader: Added support for tileset properties
+    loader: Now checks for property names that are reserved for internal use
+    loader: Added support for rotated tiles
+    pygame: Only the tiles that are used in the map will be loaded into memory
+    pygame: Added support for rotated tiles
+    pygame: Added option to force a bitsize (depth) for surfaces
+    pygame: Added option to convert alpha transparency to colorkey transparency
+    pygame: Tilesets no longer load with per-pixel alphas by default
+    pygame: Colorkey transparency should be correctly handled now
+
+
+NOTES:
+
+* The Tiled "properties" have reserved names.
+
+If you use "properties" for any of the following object types, you cannot use
+any of theese words as a name for your property.  A ValueError will be raised
+if there are any conflicts.
+
+As of 0.8.1, these values are:
+
+map:        version, orientation, width, height, tilewidth, tileheight
+            properties, tileset, layer, objectgroup
+
+tileset:    firstgid, source, name, tilewidth, tileheight, spacing, margin,
+            image, tile, properties
+
+tile:       id, image, properties
+
+layer:      name, x, y, width, height, opacity, properties, data
+
+objectgroup: name, color, x, y, width, height, opacity, object, properties
+
+object:     name, type, x, y, width, height, gid, properties, polygon,
+            polyline, image
+
+
+
+I have been intentionally not including a module rendering utility since
+rendering a map will have different requirements for every situation.  However,
+I can appreciate that some poeple won't understand how it works unless they see
+it, so I am including a sample map and viewer.  It includes a scrolling/zooming
+renderer.  They are for demonstation purposes, and may not be suitable for all
+projects.  Use at your own risk.
+
+I've also included a copy of this loader that may work with python 3.x.  I
+personally do not think that python 3.x should be used with pygame, yet (and I
+am not the only person).  You can try it if you insist on using pygame with
+python 3.x, but I don't update that often.
+
+===============================================================================
+
+Basic usage sample:
+
+    >>> from pytmx import tmxloader
+    >>> tmxdata = tmxloader.load_pygame("map.tmx")
+
+
+When you want to draw tiles, you simply call "getTileImage":
+
+    >>> image = tmxdata.getTileImage(x, y, layer)    
+    >>> screen.blit(position, image)
+
+
+Maps, tilesets, layers, objectgroups, and objects all have a simple way to
+access metadata that was set inside tiled: they all become object attributes.
+
+    >>> layer = tmxdata.layers[0]
+    >>> print layer.tilewidth
+    32
+    >>> print layer.weather
+    'sunny'
+
+
+Tiles properties are the exception here, and must be accessed through
+"getTileProperties".  The data is a regular Python dictionary:
+
+    >>> tile = tmxdata.getTileProperties(x, y, layer)
+    >>> tile["name"]
+    'CobbleStone'
+
+
+
+Please see the TiledMap class for some api information.
+"""
+from utils import types
+from constants import *
+
+
+
+def load_tmx(filename, *args, **kwargs):
+    # for .14 compatibility
+    from pytmx import TiledMap
+
+    tiledmap = TiledMap(filename)
+    return tiledmap
+
+
+def load_images_pygame(tmxdata, mapping, *args, **kwargs):
+    """
+    due to the way the tiles are loaded, they will be in the same pixel format
+    as the display when it is loaded.  take this into consideration if you
+    intend to support different screen pixel formats.
+
+    by default, the images will not have per-pixel alphas.  this can be
+    changed by including "pixelalpha=True" in the keywords.  this will result
+    in much slower blitting speeds.
+
+    if the tileset's image has colorkey transparency set in Tiled, the loader
+    will return images that have their transparency already set.  using a
+    tileset with colorkey transparency will greatly increase the speed of
+    rendering the map.
+
+    optionally, you can force the loader to strip the alpha channel of the
+    tileset image and to fill in the missing areas with a color, then use that
+    new color as a colorkey.  the resulting tiles will render much faster, but
+    will not preserve the transparency of the tile if it uses partial
+    transparency (which you shouldn't be doing anyway, this is SDL).
+    """
+    from itertools import product
+    from pygame import Surface, mask
+    import pygame, os
+
+
+    def handle_transformation(tile, flags):
+        if flags:
+            fx = flags & TRANS_FLIPX == TRANS_FLIPX
+            fy = flags & TRANS_FLIPY == TRANS_FLIPY
+            r  = flags & TRANS_ROT == TRANS_ROT
+
+            if r:
+                # not sure why the flip is required...but it is.
+                newtile = pygame.transform.rotate(tile, 270)
+                newtile = pygame.transform.flip(newtile, 1, 0)
+
+                if fx or fy:
+                    newtile = pygame.transform.flip(newtile, fx, fy)
+
+            elif fx or fy:
+                newtile = pygame.transform.flip(tile, fx, fy)
+
+            # preserve any flags that may have been lost after the transformation
+            return newtile.convert(tile)
+
+        else:
+            return tile
+
+
+    pixelalpha     = kwargs.get("pixelalpha", False)
+    force_colorkey = kwargs.get("force_colorkey", False)
+    force_bitdepth = kwargs.get("depth", False)
+
+    if force_colorkey:
+        try:
+            force_colorkey = pygame.Color(*force_colorkey)
+        except:
+            msg = "Cannot understand color: {}"
+            raise Exception, msg.format(force_colorkey)
+
+    tmxdata.images = [0] * tmxdata.maxgid
+
+    for firstgid, t in sorted((t.firstgid, t) for t in tmxdata.tilesets):
+        path = os.path.join(os.path.dirname(tmxdata.filename), t.source)
+
+        image = pygame.image.load(path)
+
+        w, h = image.get_size()
+        tile_size = (t.tilewidth, t.tileheight)
+        real_gid = t.firstgid - 1
+
+        if t.trans:
+            tileset_colorkey = pygame.Color("#{}".format(t.trans)) 
+
+        # i dont agree with margins and spacing, but i'll support it anyway
+        # such is life.  okay.jpg
+        tilewidth = t.tilewidth + t.spacing
+        tileheight = t.tileheight + t.spacing
+
+        # some tileset images may be slightly larger than the tile area
+        # ie: may include a banner, copyright, ect.  this compensates for that
+        width = ((int((w-t.margin*2) + t.spacing) / tilewidth) * tilewidth) - t.spacing
+        height = ((int((h-t.margin*2) + t.spacing) / tileheight) * tileheight) - t.spacing
+
+        # using product avoids the overhead of nested loops
+        p = product(xrange(t.margin, height+t.margin, tileheight),
+                    xrange(t.margin, width+t.margin, tilewidth))
+
+        for (y, x) in p:
+            real_gid += 1
+            gids = tmxdata.mapGID(real_gid)
+            if gids == []: continue
+
+            # we do some tests to correctly handle the tile and set the right
+            # blitting flags.  just grab a section of it.
+            temp = image.subsurface(((x,y), tile_size))
+
+            # count the number of pixels in the tile that are not transparent
+            px = mask.from_surface(temp).count()
+
+            # there are no transparent pixels in the image
+            if px == tile_size[0] * tile_size[1]:
+                tile = temp.convert()
+
+            # there are transparent pixels, and set to force a colorkey
+            elif force_colorkey:
+                tile = Surface(tile_size)
+                tile.fill(force_colorkey)
+                tile.blit(temp, (0,0))
+                tile.set_colorkey(force_colorkey, pygame.RLEACCEL)
+
+            # there are transparent pixels, and tiled set a colorkey
+            elif t.trans:
+                tile = temp.convert()
+                tile.set_colorkey(tileset_colorkey, pygame.RLEACCEL)
+
+            # there are transparent pixels, and set for perpixel alpha
+            elif pixelalpha:
+                tile = temp.convert_alpha()
+
+            # there are transparent pixels, and we won't handle them
+            else:
+                tile = temp.convert()
+
+            for gid, flags in gids:
+                tmxdata.images[gid] = handle_transformation(tile, flags)
+
+
+def load_pygame(filename, *args, **kwargs):
+    tmxdata = load_tmx(filename, *args, **kwargs)
+    load_images_pygame(tmxdata, None, *args, **kwargs)
+
+    return tmxdata
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx/tmxloader3.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,612 @@
+"""
+Map loader for TMX Files
+bitcraft (leif.theden at gmail.com)
+v.7  -- for python 3.x
+
+If you have any problems, please contact me via email.
+Tested with Tiled 0.7.1 for Mac.
+
+released under the LGPL v3
+
+======================================================================
+Design Goals:
+    Simple api
+    Memory efficient and fast
+    Quick access to tiles, attributes, and properties
+
+Non-Goals:
+    Rendering
+
+Works:
+    Pygame image loading
+    Map loading with all required types
+    Properties for all types: maps, layers, objects, tiles
+    Automatic flipping of tiles
+
+Todo:
+    Pygame: test colorkey transparency
+
+    Optimized for maps that do not make heavy use of tile
+    properties.  If i find that it is used a lot then i can rework
+    it for better performance.
+
+======================================================================
+
+Basic usage sample:
+
+    >>> import tmxloader
+    >>> tiledmap = tmxloader.load_pygame("map.tmx")
+
+
+When you want to draw tiles, you simply call "get_tile_image":
+
+    >>> image = tiledmap.get_tile_image(x, y, layer)    
+    >>> screen.blit(position, image)
+
+
+Layers, objectgroups, tilesets, and maps all have a simple way to access
+metadata that was set inside tiled: they all become class attributes.
+
+    >>> print(layer.tilewidth)
+    32
+    >>> print(layer.weather)
+    'sunny'
+
+
+Tiles are the exception here, and must be accessed through "getTileProperties"
+and are regular Python dictionaries:
+
+    >>> tile = tiledmap.getTileProperties(x, y, layer)
+    >>> tile["name"]
+    'CobbleStone'
+
+"""
+
+from itertools import chain
+
+
+# internal flags
+FLIP_X = 1
+FLIP_Y = 2
+
+
+# Tiled gid flags
+GID_FLIP_X = 1<<31
+GID_FLIP_Y = 1<<30
+
+
+class TiledElement(object):
+    pass
+
+class TiledMap(TiledElement):
+    """
+    not really useful unless "loaded"  ie: don't instance directly.
+    see the pygame loader for inspiration
+    """
+
+    def __init__(self):
+        TiledElement.__init__(self)
+        self.layers = []            # list of all layer types (tile layers + object layers)
+        self.tilesets = []          # list of TiledTileset objects
+        self.tilelayers   = []      # list of TiledLayer objects
+        self.objectgroups = []      # list of TiledObjectGroup objects
+        self.tile_properties = {}   # dict of tiles that have additional metadata (properties)
+        self.filename = None
+
+        # this is a work around to tiled's strange way of storing gid's
+        self.images = [0]
+
+        # defaults from the TMX specification
+        self.version = 0.0
+        self.orientation = None
+        self.width = 0
+        self.height = 0
+        self.tilewidth = 0
+        self.tileheight = 0 
+
+    def get_tile_image(self, x, y, layer):
+        """
+        return the tile image for this location
+        x and y must be integers and are in tile coordinates, not pixel
+
+        return value will be 0 if there is no tile with that location.
+        """
+
+        try:
+            gid = self.tilelayers[layer].data[y][x]
+        except (IndexError, ValueError):
+            msg = "Coords: ({0},{1}) in layer {2} is invalid.".format(x, y, layer)
+            raise Exception(msg)
+
+        else:
+            try:
+                return self.images[gid]
+            except (IndexError, ValueError):
+                msg = "Coords: ({0},{1}) in layer {2} has invalid GID: {3}/{4}.".format(x, y, layer, gid, len(self.images))
+                raise Exception(msg)
+
+    def getTileGID(self, x, y, layer):
+        """
+        return GID of a tile in this location
+        x and y must be integers and are in tile coordinates, not pixel
+        """
+
+        try:
+            return self.tilelayers[layer].data[y][x]
+        except (IndexError, ValueError):
+            msg = "Coords: ({0},{1}) in layer {2} is invalid.".format(x, y, layer)
+            raise Exception(msg)
+
+    def getDrawOrder(self):
+        """
+        return a list of objects in the order that they should be drawn
+        this will also exclude any layers that are not set to visible
+
+        may be useful if you have objects and want to control rendering
+        from tiled
+        """
+
+        raise NotImplementedError
+
+    def getTileImages(self, r, layer):
+        """
+        return a group of tiles in an area
+        expects a pygame rect or rect-like list/tuple
+
+        usefull if you don't want to repeatedly call get_tile_image
+        probably not the most effecient way of doing this, but oh well.
+        """
+
+        raise NotImplementedError
+
+    def getObjects(self):
+        """
+        Return iterator all of the objects associated with this map
+        """
+
+        return chain(*[ i.objects for i in self.objectgroups ])
+
+    def getTileProperties(self, x, y, layer):
+        """
+        return the properties for the tile, if any
+        x and y must be integers and are in tile coordinates, not pixel
+
+        returns a dict of there are properties, otherwise will be None
+        """
+
+        try:
+            gid = self.tilelayers[layer].data[y][x]
+        except (IndexError, ValueError):
+            msg = "Coords: ({0},{1}) in layer {2} is invalid.".format(x, y, layer)
+            raise Exception(msg)
+
+        else:
+            try:
+                return self.tile_properties[gid]
+            except (IndexError, ValueError):
+                msg = "Coords: ({0},{1}) in layer {2} has invaid GID: {3}/{4}.".format(x, y, layer, gid, len(self.images))
+                raise Exception(msg)
+
+    def getTilePropertiesByGID(self, gid):
+        try:
+            return self.tile_properties[gid]
+        except KeyError:
+            return None
+
+# the following classes get their attributes filled in with the loader
+
+class TiledTileset(TiledElement):
+    def __init__(self):
+        TiledElement.__init__(self)
+
+        # defaults from the specification
+        self.firstgid = 0
+        self.lastgid = 0
+        self.name = None
+        self.tilewidth = 0
+        self.tileheight = 0
+        self.spacing = 0
+        self.margin = 0
+
+class TiledLayer(TiledElement):
+    def __init__(self):
+        TiledElement.__init__(self)
+        self.data = None
+
+        # defaults from the specification
+        self.name = None
+        self.opacity = 1.0
+        self.visible = 1
+        
+class TiledObjectGroup(TiledElement):
+    def __init__(self):
+        TiledElement.__init__(self)
+        self.objects = []
+
+        # defaults from the specification
+        self.name = None
+
+class TiledObject(TiledElement):
+    __slots__ = ['name', 'type', 'x', 'y', 'width', 'height', 'gid']
+
+    def __init__(self):
+        TiledElement.__init__(self)
+
+        # defaults from the specification
+        self.name = None
+        self.type = None
+        self.x = 0
+        self.y = 0
+        self.width = 0
+        self.height = 0
+        self.gid = 0
+
+
+def load_tmx(filename):
+    """
+    Utility function to parse a Tiled TMX and return a usable object.
+    Images will not be loaded, so probably not useful to call this directly
+
+    See the load_pygame func for an idea of what to do
+    """
+
+    from xml.dom.minidom import parse
+    from itertools import tee, islice, chain
+    from collections import defaultdict 
+    from struct import unpack
+    import array, os
+
+    # used to change the unicode string returned from minidom to
+    # proper python variable types.
+    types = {
+        "version": float,
+        "orientation": str,
+        "width": int,
+        "height": int,
+        "tilewidth": int,
+        "tileheight": int,
+        "firstgid": int,
+        "source": str,
+        "name": str,
+        "spacing": int,
+        "margin": int,
+        "source": str,
+        "trans": str,
+        "id": int,
+        "opacity": float,
+        "visible": bool,
+        "encoding": str,
+        "compression": str,
+        "gid": int,
+        "type": str,
+        "x": int,
+        "y": int,
+        "value": str,
+    }
+ 
+    def pairwise(iterable):
+        # return a list as a sequence of pairs
+        a, b = tee(iterable)
+        next(b, None)
+        return zip(a, b)
+
+    def group(l, n):
+        # return a list as a sequence of n tuples
+        return zip(*[islice(l, i, None, n) for i in range(n)])
+
+    def parse_properties(node):
+        """
+        parse a node and return a dict that represents a tiled "property"
+        """
+
+        d = {}
+
+        for child in node.childNodes:
+            if child.nodeName == "properties":
+                for subnode in child.getElementsByTagName("property"):
+                    # the "properties" from tiled's tmx have an annoying
+                    # quality that "name" and "value" is included as part of it.
+                    # so we mangle it to get that stuff out.
+                    d.update(dict(pairwise([ str(i.value) for i in list(subnode.attributes.values()) ])))
+
+        return d
+
+    def get_properties(node):
+        """
+        parses a node and returns a dict that contains the data from the node's
+        attributes and any data from "property" elements as well.
+        """
+
+        d = {}
+
+        # get tag attributes
+        d.update(get_attributes(node))
+
+        # get vlues of the properties element, if any
+        d.update(parse_properties(node))
+     
+        return d
+
+    def set_properties(obj, node):
+        """
+        read the xml attributes and tiled "properties" from a xml node and fill in
+        the values into an object's dictionary
+        """
+
+        [ setattr(obj, k, v) for k,v in list(get_properties(node).items()) ]
+
+    def get_attributes(node):
+        """
+        get the attributes from a node and fix them to the correct type
+        """
+
+        d = defaultdict(lambda:None)
+
+        for k, v in list(node.attributes.items()):
+            k = str(k)
+            d[k] = types[k](v)
+
+        return d
+
+    def decode_gid(raw_gid):
+        # gid's are encoded with extra information
+        # as of 0.7.0 it determines if the tile should be flipped when rendered
+
+        flags = 0
+        if raw_gid & GID_FLIP_X == GID_FLIP_X: flags += FLIP_X
+        if raw_gid & GID_FLIP_Y == GID_FLIP_Y: flags += FLIP_Y
+        gid = raw_gid & ~(GID_FLIP_X | GID_FLIP_Y)
+
+        return gid, flags
+
+
+    def parse_map(node):
+        """
+        parse a map node from a tiled tmx file
+        return a tiledmap
+        """
+
+        tiledmap = TiledMap()
+        tiledmap.filename = filename
+        set_properties(tiledmap, map_node)
+
+        for node in map_node.getElementsByTagName("tileset"):
+            t, tiles = parse_tileset(node)
+            tiledmap.tilesets.append(t)
+            tiledmap.tile_properties.update(tiles)
+
+        for node in dom.getElementsByTagName("layer"):
+            l = parse_layer(tiledmap.tilesets, node)
+            tiledmap.tilelayers.append(l)
+            tiledmap.layers.append(l)
+
+        for node in dom.getElementsByTagName("objectgroup"):
+            o = parse_objectgroup(node)
+            tiledmap.objectgroups.append(o)
+            tiledmap.layers.append(o)
+
+        return tiledmap
+
+
+    def parse_tileset(node, firstgid=None):
+        """
+        parse a tileset element and return a tileset object and properties for tiles as a dict
+        """
+
+        tileset = TiledTileset()
+        set_properties(tileset, node)
+        tiles = {}
+
+        if firstgid != None:
+            tileset.firstgid = firstgid
+
+        # since tile objects probably don't have a lot of metadata,
+        # we store it seperately from the class itself
+        for child in node.childNodes:
+            if child.nodeName == "tile":
+                p = get_properties(child)
+                gid = p["id"] + tileset.firstgid
+                del p["id"]
+                tiles[gid] = p
+
+        # check for tiled "external tilesets"
+        if hasattr(tileset, "source"):
+            if tileset.source[-4:].lower() == ".tsx":
+                try:
+                    # we need to mangle the path some because tiled stores relative paths
+                    path = os.path.join(os.path.dirname(filename), tileset.source)
+                    tsx = parse(path)
+                except IOError:
+                    raise IOError("Cannot load external tileset: " + path)
+
+                tileset_node = tsx.getElementsByTagName("tileset")[0]
+                tileset, tiles = parse_tileset(tileset_node, tileset.firstgid)
+            else:
+                raise Exception("Found external tileset, but cannot handle type: " + tileset.source)
+
+        # if we have an "image" tag, process it here
+        try:
+            image_node = node.getElementsByTagName("image")[0]
+        except IndexError:
+            pass
+        else:
+            attr = get_attributes(image_node)
+            tileset.source = attr["source"]
+            tileset.trans = attr["trans"]
+
+            # calculate the number of tiles in this tileset
+            x, r = divmod(attr["width"], tileset.tilewidth)
+            y, r = divmod(attr["height"], tileset.tileheight)
+
+            tileset.lastgid = tileset.firstgid + x + y
+          
+        return tileset, tiles
+
+
+    def parse_layer(tilesets, node):
+        """
+        parse a layer element and return a layer object
+
+        tilesets is required since we need to mangle gid's here
+        """
+
+        layer = TiledLayer()
+        layer.data = []
+        layer.flipped_tiles = []
+        set_properties(layer, node)
+
+        data = None
+        next_gid = None
+
+        data_node = node.getElementsByTagName("data")[0]
+        attr = get_attributes(data_node)
+
+        if attr["encoding"] == "base64":
+            from base64 import b64decode
+            data = b64decode(bytes(data_node.lastChild.nodeValue, 'ascii'))
+
+        elif attr["encoding"] == "csv":
+            next_gid = map(int, "".join([line.strip() for line in data_node.lastChild.nodeValue]).split(","))
+
+        elif not attr["encoding"] == None:
+            raise Exception("TMX encoding type: " + str(attr["encoding"]) + " is not supported.")
+      
+        if attr["compression"] == "gzip":
+            from io import BytesIO
+            import gzip
+            with gzip.GzipFile(fileobj=BytesIO(data)) as fh:
+                data = fh.read()
+
+        elif not attr["compression"] == None:
+            raise Exception("TMX compression type: " + str(attr["compression"]) + " is not supported.")
+     
+        # if data is None, then it was not decoded or decompressed, so
+        # we assume here that it is going to be a bunch of tile elements
+        if attr["encoding"] == next_gid == None:
+            def get_children(parent):
+                for child in parent.getElementsByTagName("tile"):
+                    yield int(child.getAttribute("gid"))
+
+            next_gid = get_children(data_node)
+
+        elif not data == None:
+            # cast the data as a list of 32-bit integers
+            def u(i): return unpack("<L", bytes(i))[0]
+            next_gid = map(u, group(data, 4))
+
+        gids = [ i.firstgid for i in tilesets ]
+
+        # fill up our 2D array of gid's.
+        for y in range(layer.height):
+
+            # store as 16-bit ints, since we will never use enough tiles to fill a 32-bit int
+            layer.data.append(array.array("H"))
+
+            for x in range(layer.width):
+                gid, flags = decode_gid(next(next_gid))
+                if not flags == 0: layer.flipped_tiles.append((x, y, gid, flags))
+                layer.data[y].append(gid)
+
+        return layer
+
+
+    def parse_objectgroup(node):
+        """
+        parse a objectgroup element and return a object group
+        """
+
+        objgroup = TiledObjectGroup()
+        set_properties(objgroup, node)
+
+        for subnode in node.getElementsByTagName("object"):
+            obj = TiledObject()
+            set_properties(obj, subnode)
+            objgroup.objects.append(obj)
+
+        return objgroup
+
+
+    # open and read our TMX (which is really just xml)
+    dom = parse(filename)
+    map_node =  dom.getElementsByTagName("map")[0]
+    return parse_map(map_node)
+
+
+def load_pygame(filename):
+    """
+    load a tiled TMX map for use with pygame
+    """
+
+    from pygame import Surface
+    import pygame, os
+
+    tiledmap = load_tmx(filename)
+
+    # cache will find duplicate tiles to reduce memory usage
+    # mostly this is a problem in the blank areas of a tilemap
+    cache = {}
+
+    # just a precaution to make sure tileset images are added in the correct order
+    for firstgid, t in sorted([ (t.firstgid, t) for t in tiledmap.tilesets ]):
+        path = os.path.join(os.path.dirname(tiledmap.filename), t.source)
+
+        image = pygame.image.load(path)
+
+        w, h = image.get_rect().size
+
+        tile_size = (t.tilewidth, t.tileheight)
+
+        # some tileset images may be slightly larger than the tiles area
+        # ie: may include a banner, copyright, ect.  this compensates for that
+        for y in range(0, int(h / t.tileheight) * t.tileheight, t.tileheight):
+            for x in range(0, int(w / t.tilewidth) * t.tilewidth, t.tilewidth):
+
+                # somewhat handle transparency, though colorkey handling is not tested
+                if t.trans == None:
+                    tile = Surface(tile_size, pygame.SRCALPHA)
+                else:
+                    tile = Surface(tile_size)
+
+                # blit the smaller portion onto a new image from the larger one
+                tile.blit(image, (0,0), ((x, y), tile_size))
+
+                # make a unique id for this image, not sure if this is the best way, but it works
+                key = pygame.image.tostring(tile, "RGBA")
+        
+                # make sure we don't have a duplicate tile
+                try:
+                    tile = cache[key]
+                except KeyError:
+                    if t.trans == None:
+                        tile = tile.convert_alpha()
+                    else:
+                        tile = tile.convert()
+                        tile.set_colorkey(t.trans)
+
+                    # update the cache
+                    cache[key] = tile
+
+                tiledmap.images.append(tile)
+
+    # correctly handle transformed tiles.  currently flipped tiles
+    # work by creating a new gid for the flipped tile and changing the gid
+    # in the layer to the new gid.
+    for layer in tiledmap.tilelayers:
+        for x, y, gid, trans in layer.flipped_tiles:
+            fx = trans & FLIP_X == FLIP_X
+            fy = trans & FLIP_Y == FLIP_Y
+
+            tile = pygame.transform.flip(tiledmap.images[gid], fx, fy)
+            tiledmap.images.append(tile)
+
+            # change the original gid in the layer data to the new gid
+            layer.data[y][x] = len(tiledmap.images) - 1
+
+        del layer.flipped_tiles
+    del cache
+
+    return tiledmap
+
+if __name__ == '__main__':
+    print('[tmxloader] starting built-in test')
+    load_tmx("../resources/maps/village.tmx")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx/utils.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,251 @@
+from pygame import Rect
+from itertools import tee, islice, izip, product
+from collections import defaultdict
+from constants import *
+
+
+
+"""
+Various code that doesn't really fit anywhere else
+"""
+
+
+def read_points(text):
+    return [ tuple(map(lambda x: int(x), i.split(',')))
+         for i in text.split() ]
+
+
+
+def parse_properties(node):
+    """
+    parse a node and return a dict that represents a tiled "property"
+    """
+
+    # the "properties" from tiled's tmx have an annoying quality that "name"
+    # and "value" is included. here we mangle it to get that junk out.
+
+    d = {}
+
+    for child in node.findall('properties'):
+        for subnode in child.findall('property'):
+            d[subnode.get('name')] = subnode.get('value')
+
+    return d
+
+
+def decode_gid(raw_gid):
+    # gid's are encoded with extra information
+    # as of 0.7.0 it determines if the tile should be flipped when rendered
+    # as of 0.8.0 bit 30 determines if GID is rotated
+
+    flags = 0
+    if raw_gid & GID_TRANS_FLIPX == GID_TRANS_FLIPX: flags += TRANS_FLIPX
+    if raw_gid & GID_TRANS_FLIPY == GID_TRANS_FLIPY: flags += TRANS_FLIPY
+    if raw_gid & GID_TRANS_ROT == GID_TRANS_ROT: flags += TRANS_ROT
+    gid = raw_gid & ~(GID_TRANS_FLIPX | GID_TRANS_FLIPY | GID_TRANS_ROT)
+
+    return gid, flags
+
+
+def handle_bool(text):
+    # properly convert strings to a bool
+    try:
+        return bool(int(text))
+    except:
+        pass
+
+    try:
+        text = str(text).lower()
+        if text == "true":   return True
+        if text == "yes":    return True
+        if text == "false":  return False
+        if text == "no":     return False
+    except:
+        pass
+        
+    raise ValueError
+
+
+# used to change the unicode string returned from xml to proper python 
+# variable types.
+types = defaultdict(lambda: str)
+types.update({
+    "version": float,
+    "orientation": str,
+    "width": int,
+    "height": int,
+    "tilewidth": int,
+    "tileheight": int,
+    "firstgid": int,
+    "source": str,
+    "name": str,
+    "spacing": int,
+    "margin": int,
+    "source": str,
+    "trans": str,
+    "id": int,
+    "opacity": float,
+    "visible": handle_bool,
+    "encoding": str,
+    "compression": str,
+    "gid": int,
+    "type": str,
+    "x": int,
+    "y": int,
+    "value": str,
+})
+
+
+def pairwise(iterable):
+    # return a list as a sequence of pairs
+    a, b = tee(iterable)
+    next(b, None)
+    return izip(a, b)
+
+
+def group(l, n):
+    # return a list as a sequence of n tuples
+    return izip(*(islice(l, i, None, n) for i in xrange(n)))
+
+
+def buildDistributionRects(tmxmap, layer, tileset=None, real_gid=None):
+    """
+    generate a set of non-overlapping rects that represents the distribution
+    of the specfied gid.
+
+    useful for generating rects for use in collision detection
+    """
+    
+    if isinstance(tileset, int):
+        try:
+            tileset = tmxmap.tilesets[tileset]
+        except IndexError:
+            msg = "Tileset #{} not found in map {}."
+            raise IndexError, msg.format(tileset, tmxmap)
+
+    elif isinstance(tileset, str):
+        try:
+            tileset = [ t for t in tmxmap.tilesets if t.name == tileset ].pop()
+        except IndexError:
+            msg = "Tileset \"{}\" not found in map {}."
+            raise ValueError, msg.format(tileset, tmxmap)
+
+    elif tileset:
+        msg = "Tileset must be either a int or string. got: {}"
+        raise ValueError, msg.format(type(tileset))
+
+    gid = None
+    if real_gid:
+        try:
+            gid, flags = tmxmap.mapGID(real_gid)[0]
+        except IndexError:
+            msg = "GID #{} not found"
+            raise ValueError, msg.format(real_gid)
+
+
+    if isinstance(layer, int):
+        layer_data = tmxmap.getLayerData(layer).data
+    elif isinstance(layer, str):
+        try:
+            layer = [ l for l in tmxmap.tilelayers if l.name == layer ].pop()
+            layer_data = layer.data
+        except IndexError:
+            msg = "Layer \"{}\" not found in map {}."
+            raise ValueError, msg.format(layer, tmxmap)
+
+    p = product(xrange(tmxmap.width), xrange(tmxmap.height))
+    if gid:
+        points = [ (x,y) for (x,y) in p if layer_data[y][x] == gid ]
+    else:
+        points = [ (x,y) for (x,y) in p if layer_data[y][x] ]
+
+    rects = simplify(points, tmxmap.tilewidth, tmxmap.tileheight)
+    return rects
+
+
+def simplify(all_points, tilewidth, tileheight):
+    """
+    klugde:
+
+    "A kludge (or kluge) is a workaround, a quick-and-dirty solution,
+    a clumsy or inelegant, yet effective, solution to a problem, typically
+    using parts that are cobbled together."
+
+    -- wikipedia
+   
+    turn a list of points into a rects 
+    adjacent rects will be combined.
+
+    plain english:
+        the input list must be a a list of tuples that represent
+        the areas to be combined into rects
+        the rects will be blended together over solid groups
+
+        so if data is something like:
+
+        0 1 1 1 0 0 0 
+        0 1 1 0 0 0 0 
+        0 0 0 0 0 4 0 
+        0 0 0 0 0 4 0
+        0 0 0 0 0 0 0 
+        0 0 1 1 1 1 1
+
+        you'll have the 4 rects that mask the area like this:
+
+        ..######......
+        ..####........
+        ..........##..
+        ..........##..
+        ..............
+        ....##########
+
+        pretty cool, right?
+
+    there may be cases where the number of rectangles is not as low as possible,
+    but i haven't found that it is excessively bad.  certainly much better than
+    making a list of rects, one for each tile on the map!
+
+    """
+
+    def pick_rect(points, rects):
+        ox, oy = sorted([ (sum(p), p) for p in points ])[0][1]
+        x = ox
+        y = oy
+        ex = None            
+
+        while 1:
+            x += 1
+            if not (x, y) in points:
+                if ex == None:
+                    ex = x - 1
+
+                if ((ox, y+1) in points):
+                    if x == ex + 1 :
+                        y += 1
+                        x = ox
+
+                    else:
+                        y -= 1
+                        break
+                else:
+                    if x <= ex: y-= 1
+                    break
+
+        c_rect = Rect(ox*tilewidth,oy*tileheight,\
+                     (ex-ox+1)*tilewidth,(y-oy+1)*tileheight)
+
+        rects.append(c_rect)
+
+        rect = Rect(ox,oy,ex-ox+1,y-oy+1)
+        kill = [ p for p in points if rect.collidepoint(p) ]
+        [ points.remove(i) for i in kill ]
+
+        if points:
+            pick_rect(points, rects)
+
+    rect_list = []
+    while all_points:
+        pick_rect(all_points, rect_list)
+
+    return rect_list
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/README	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,44 @@
+Map loader for TMX Files
+bitcraft (leif dot theden at gmail.com)
+v.15 - for python 2.7
+
+If you have any problems or suggestions, please contact me via email.
+Tested with Tiled 0.8.0 for Mac.
+
+released under the LGPL v3
+
+
+===============================================================================
+
+This map loader can be used to load maps created in the Tiled map editor.  It
+provides a simple way to get tiles and associated metadata so that you can draw
+a map onto the screen.
+
+This is *not* a rendering engine.  It will load the data that is necessary to
+render a map onto the screen.  All tiles will be loaded into in memory and
+available to blit onto the screen.
+
+
+Design Goals:
+    Simple api
+    Memory efficient and fast
+
+Features:
+    Loads data and "properties" metadata from Tile's TMX format
+    "Properties" for: maps, tilesets, layers, objectgroups, objects, and tiles
+    Point data for polygon and polyline objects
+    Automatic flipping and rotation of tiles
+    Supports base64, csv, gzip, zlib and uncompressed XML
+    Image loading with pygame
+
+
+===============================================================================
+
+Please see tmxloader.py's docstring for version information and sample usage.
+Check tests/test.py and tests/demo.py for examples on how to use the library.
+
+
+===============================================================================
+
+The 16x16 overworld tiles were created by MrBeast at opengameart.org. CC-BY 3.0
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/lgpl-3.0.txt	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
Binary file 002/pytmx_docs/tests/16x16-overworld.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/16x16-overworld.tsx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tileset name="16x16-overworld" tilewidth="16" tileheight="16">
+ <image source="16x16-overworld.png" width="256" height="336"/>
+ <tile id="17">
+  <properties>
+   <property name="name" value="grass"/>
+  </properties>
+ </tile>
+ <tile id="275">
+  <properties>
+   <property name="name" value="castle-door"/>
+  </properties>
+ </tile>
+</tileset>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/demo.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,237 @@
+"""
+This is tested on pygame 1.9 and python 2.7.
+This will not work on python 3.  Don't ask either.  I will say 'no'.
+bitcraft (leif dot theden at gmail.com)
+
+Rendering demo for the TMXLoader.  This simply shows that the loader works.
+If you need a rendering library that will handle large maps and scrolling, you
+can check out my lib2d project at pygame.org.  Have fun!
+
+
+Known bugs:
+    Tile Objects are not handled by any renderer.
+
+
+"""
+
+class TiledRenderer(object):
+    """
+    Super simple way to render a tiled map
+    """
+
+    def __init__(self, filename):
+        from pytmx import tmxloader
+        self.tiledmap = tmxloader.load_pygame(filename, pixelalpha=True)
+   
+ 
+    def render(self, surface):
+        # not going for effeciency here
+        # for demonstration purposes only
+
+        tw = self.tiledmap.tilewidth
+        th = self.tiledmap.tileheight
+        gt = self.tiledmap.getTileImage
+
+        for l in xrange(0, len(self.tiledmap.tilelayers)):
+            for y in xrange(0, self.tiledmap.height):
+                for x in xrange(0, self.tiledmap.width):
+                    tile = gt(x, y, l)
+                    if tile: surface.blit(tile, (x*tw, y*th))
+
+
+class ScrollingRenderer(TiledRenderer):
+    """
+    Simple way for rendering a scrolling map that is larger than the display.
+
+                 !!!SUPER IMPORTANT NOTE - YOU MUST READ!!!
+
+    THIS IS NOT THE CORRECT WAY TO DO SCROLLING GAMES IN PYGAME!  THE
+    UNDERLYING LIBRARY, SDL, IS NOT SUITED FOR REAL-TIME SCROLLING GAMES.  THIS
+    IS ONLY A DEMONSTRATION ON SCROLLING A MAP USING THIS LIBRARY AND IS IN NO
+    WAY IMPLIED TO BE THE BEST OR CORRECT WAY.
+    """
+
+    def __init__(self, filename):
+        super(ScrollingRenderer, self).__init__(filename)
+        self.width = self.tiledmap.width * self.tiledmap.tilewidth
+        self.height = self.tiledmap.height * self.tiledmap.tileheight
+
+        self.mapwidth = self.tiledmap.width
+        self.mapheight = self.tiledmap.height
+
+        self.halfwidth = self.tiledmap.width / 2
+        self.halfheight = self.tiledmap.height / 2 + 1
+
+
+    def render(self, surface, (cx, cy)):
+        sw, sh = surface.get_size()
+        tw = self.tiledmap.tilewidth
+        th = self.tiledmap.tileheight
+        gt = self.tiledmap.getTileImage
+
+        stw = int(math.ceil(float(sw) / tw)) + 1 
+        sth = int(math.ceil(float(sh) / th)) + 1
+
+        txf, pxf = divmod((cx-sw/2), tw)
+        tyf, pyf = divmod((cy-sh/2), th)
+
+        if stw + txf > self.mapwidth: stw -= 1
+        if sth + tyf > self.mapheight: sth -= 1
+
+        p = product(xrange(stw), xrange(sth),
+                    xrange(len(self.tiledmap.tilelayers)))
+
+        for x, y, l in p:
+            tile = gt(x+txf, y+tyf, l)
+            if tile: surface.blit(tile, (x*tw-pxf, y*th-pyf))
+
+
+
+import pygame
+from pygame.locals import *
+import math
+from itertools import product
+
+
+pygame.init()
+pygame.font.init()
+screen = pygame.display.set_mode((480, 480))
+pygame.display.set_caption('TMXLoader Test')
+
+
+def simpleTest(filename):
+    screen_buf = pygame.Surface((240, 240))
+    screen_buf.fill((0,128,255))
+    formosa = TiledRenderer(filename)
+    formosa.render(screen_buf)
+    pygame.transform.scale(screen_buf, screen.get_size(), screen)
+    f = pygame.font.Font(pygame.font.get_default_font(), 20)
+    i = f.render("simple demo. press any key to continue", 1, (180,180,0))
+    screen.blit(i, (0,0))
+    pygame.display.flip()
+
+    run = True
+    while run:
+        try:
+            event = pygame.event.wait()
+            if (event.type == QUIT) or (event.type == KEYDOWN): run = False
+
+        except KeyboardInterrupt:
+            run = False
+
+
+def scrollTest(filename):
+    buf_dim = [screen.get_width() / 2, screen.get_height() / 2]
+    center = [buf_dim[0]/2, buf_dim[1]/2]
+    movt = [0, 0, 0]
+
+    clock = pygame.time.Clock()
+    screen_buf = pygame.Surface(buf_dim)
+    formosa = ScrollingRenderer(filename)
+    mw = formosa.tiledmap.width * formosa.tiledmap.tilewidth
+    mh = formosa.tiledmap.height * formosa.tiledmap.tileheight
+
+    f = pygame.font.Font(pygame.font.get_default_font(), 20)
+    t = ["scroll demo. press escape to quit",
+         "arrow keys move",
+         "z and x will zoom the map"]
+
+    text = [ f.render(i, 1, (180, 180, 0)) for i in t ]
+
+    def draw():
+        bw, bh = screen_buf.get_size()
+        sw, sh = screen.get_size()
+
+        if (sw >= bw) and (sh >= bh):
+            y = 0
+            screen_buf.fill((0,128,255))
+            formosa.render(screen_buf, center)
+            pygame.transform.smoothscale(screen_buf, (sw, sh), screen)
+            for i in text:
+                screen.blit(i, (0,y))
+                y += i.get_height()
+        else:
+            pass
+
+
+    draw()
+    run = True
+    while run:
+        try:
+            clock.tick(30)
+            event = pygame.event.poll()
+
+            if event.type == QUIT: run = False
+            elif event.type == KEYDOWN:
+                if event.key == K_z:
+                    movt[2] -= 2
+                if event.key == K_x:
+                    movt[2] += 2
+                elif event.key == K_UP:
+                    movt[1] -= 1
+                elif event.key == K_DOWN:
+                    movt[1] += 1
+                elif event.key == K_LEFT:
+                    movt[0] -= 1
+                elif event.key == K_RIGHT:
+                    movt[0] += 1
+                elif event.key == K_ESCAPE:
+                    run = False
+
+            center[0] += movt[0]
+            center[1] += movt[1]
+            if not movt[2] == 0:
+                buf_dim[0] += movt[2]
+                buf_dim[1] += movt[2]
+                if (buf_dim[0] < 1) or (buf_dim[1] < 0):
+                    buf_dim[0] += 1 - buf_dim[0]
+                    buf_dim[1] += 1 - buf_dim[1]
+                if buf_dim[0] > screen.get_width() / 2:
+                    buf_dim = [screen.get_width() / 2, screen.get_height() / 2]
+                    movt[2] = 0
+                screen_buf = pygame.Surface(buf_dim)
+
+            sw, sh = screen_buf.get_size()
+            hsw = sw / 2
+            hsh = sh / 2
+
+            if formosa.width > sw:
+                if center[0] < hsw:
+                    center[0] = hsw
+                    movt[0] = 0
+                elif center[0] > mw - hsw-1:
+                    center[0] = mw - hsw-1 
+                    movt[0] = 0
+            else:
+                center[0] = formosa.width / 2
+
+            if formosa.height > sh:
+                if center[1] < hsh:
+                    center[1] = hsh
+                    movt[1] = 0
+                elif center[1] > mh - hsh - 1:
+                    center[1] = mh - hsh - 1
+                    movt[1] = 0
+            else:
+                center[1] = formosa.height / 2
+
+            draw()
+            pygame.display.flip()
+
+
+        except KeyboardInterrupt:
+            run = False
+
+
+if __name__ == "__main__":
+    import sys
+    try:
+        filename = sys.argv[1]
+    except:
+        print "no TMX map specified, using default"
+        filename = "formosa-base64-gzip.tmx"
+
+    simpleTest(filename)
+    scrollTest(filename)
+
+    pygame.quit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/desert.tmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="40" height="40" tilewidth="32" tileheight="32">
+ <tileset firstgid="1" name="Desert" tilewidth="32" tileheight="32" spacing="1" margin="1">
+  <image source="tmw_desert_spacing.png" width="265" height="199"/>
+ </tileset>
+ <layer name="Ground" width="40" height="40">
+  <data encoding="base64" compression="gzip">
+   H4sIAAAAAAAAA+2Y2QrCMBBFo31wA+sCrlirdd/q/3+dIzZUhtAmMRlH6cPBBlJyvDO2g5EQIvJIFwiBnudzbLgp/JYMvH4lv2dWI2AMTBj4qKgBdSBA3j7OmgIzA+ZAC2gDHYIsVsDagMSj36HAbwOcgO0X/YrySzTzpPDbMfS7Iy/sdxGv/r8S++Gew+uyvHBfuvBT/Q4OKD95XeaHc3WV3x6tpY9pfr7riz0xx6x+upwd+DUymiJ/FxURaO6TLD70C0U+X8jvWkRHc5/qPhu/scjniz4wKGGouU91n8/eoyYW6jmHk2dM6GMzm1F43bJPnWfn+/sncXS+Ljp+T6d7tpfSL2Xoh/uam59Nfd/nCyq/1MAPZ8ktP1WtOftR5mczm1H6/QsxA4dfAWfF7b/BqsYVFZ/xABSXR5cAGQAA
+  </data>
+ </layer>
+</map>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/formosa-base64-gzip.tmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16">
+ <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16">
+  <image source="16x16-overworld.png" width="256" height="336"/>
+  <tile id="17">
+   <properties>
+    <property name="name" value="grass"/>
+   </properties>
+  </tile>
+  <tile id="245">
+   <properties>
+    <property name="type" value="GrassCave"/>
+   </properties>
+  </tile>
+  <tile id="261">
+   <properties>
+    <property name="type" value="SandCave"/>
+   </properties>
+  </tile>
+  <tile id="275">
+   <properties>
+    <property name="name" value="castle-door"/>
+   </properties>
+  </tile>
+ </tileset>
+ <layer name="Grass and Water" width="15" height="15">
+  <data encoding="base64" compression="gzip">
+   H4sIAAAAAAAAAxNiYGAQGsXDEmdToI8cvdlk6s0mUy+6PkJ6sakfDBgA3TcUToQDAAA=
+  </data>
+ </layer>
+ <layer name="Tile Layer 1" width="15" height="15">
+  <data encoding="base64" compression="gzip">
+   H4sIAAAAAAAAA92OSw5AMBRF3wuCMYIplsAG2Jg9o5i5kopPqtTQTU7S33m9RGbpQA8GuRdgBBOYH1yLiWzg8H7mYu0Bn/VzAtyHIDq4MdYJSPk8Ryj8gqm963Wco4rO3SLI/F9VGnSoJKWhW1761ybyxct/6K3vM4nKyzR86fc2Cx4LRKqEAwAA
+  </data>
+ </layer>
+ <layer name="Tile Layer 2" width="15" height="15">
+  <data encoding="base64" compression="gzip">
+   H4sIAAAAAAAAA2NgGAXEAi9GBgcgPkCOXiugXiuoXi1GhgQgOwFoVgIxeoHqHbSgeoF6FgD1LgDyF5DjDlIA0A4QboCySXIzUC0Ig/WS6magehBuIMvRQxAAAAvGQyuEAwAA
+  </data>
+ </layer>
+ <objectgroup name="Object Layer 1" width="15" height="15">
+  <object name="Castle" x="192" y="16">
+   <properties>
+    <property name="Population" value="234"/>
+   </properties>
+   <polygon points="0,0 0,32 48,32 48,0"/>
+  </object>
+  <object name="Towers Area" x="0" y="192">
+   <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/>
+  </object>
+  <object name="Coastline" x="0" y="208">
+   <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/>
+  </object>
+  <object name="GenericObject" x="96" y="64" width="32" height="32"/>
+  <object name="SandCave" gid="262" x="64" y="80"/>
+  <object name="Road" x="224" y="48">
+   <properties>
+    <property name="MoveCost" value="2"/>
+   </properties>
+   <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/>
+  </object>
+ </objectgroup>
+</map>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/formosa-base64-zlib.tmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16">
+ <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16">
+  <image source="16x16-overworld.png" width="256" height="336"/>
+  <tile id="17">
+   <properties>
+    <property name="name" value="grass"/>
+   </properties>
+  </tile>
+  <tile id="245">
+   <properties>
+    <property name="type" value="GrassCave"/>
+   </properties>
+  </tile>
+  <tile id="261">
+   <properties>
+    <property name="type" value="SandCave"/>
+   </properties>
+  </tile>
+  <tile id="275">
+   <properties>
+    <property name="name" value="castle-door"/>
+   </properties>
+  </tile>
+ </tileset>
+ <layer name="Grass and Water" width="15" height="15">
+  <data encoding="base64" compression="zlib">
+   eJwTYmBgEBrFwxJnU6CPHL3ZZOrNJlMvuj5CerGpHwwYAHdzIkA=
+  </data>
+ </layer>
+ <layer name="Tile Layer 1" width="15" height="15">
+  <data encoding="base64" compression="zlib">
+   eJzdjksOQDAURd8LgjGCKZbABtiYPaOYuZKKT6rU0E1O0t95vURm6UAPBrkXYAQTmB9ci4ls4PB+5mLtAZ/1cwLchyA6uDHWCUj5PEco/IKpvet1nKOKzt0iyPxfVRp0qCSloVte+tcm8sXLf+it7zOJyss0fOn3NgsYKxS4
+  </data>
+ </layer>
+ <layer name="Tile Layer 2" width="15" height="15">
+  <data encoding="base64" compression="zlib">
+   eJxjYBgFxAIvRgYHID5Ajl4roF4rqF4tRoYEIDsBaFYCMXqB6h20oHqBehYA9S4A8heQ4w5SANAOEG6AsklyM1AtCIP1kupmoHoQbiDL0UMQAAAYyhAJ
+  </data>
+ </layer>
+ <objectgroup name="Object Layer 1" width="15" height="15">
+  <object name="Castle" x="192" y="16">
+   <properties>
+    <property name="Population" value="234"/>
+   </properties>
+   <polygon points="0,0 0,32 48,32 48,0"/>
+  </object>
+  <object name="Towers Area" x="0" y="192">
+   <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/>
+  </object>
+  <object name="Coastline" x="0" y="208">
+   <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/>
+  </object>
+  <object name="GenericObject" x="96" y="64" width="32" height="32"/>
+  <object name="SandCave" gid="262" x="64" y="80"/>
+  <object name="Road" x="224" y="48">
+   <properties>
+    <property name="MoveCost" value="2"/>
+   </properties>
+   <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/>
+  </object>
+ </objectgroup>
+</map>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/formosa-base64.tmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16">
+ <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16">
+  <image source="16x16-overworld.png" width="256" height="336"/>
+  <tile id="17">
+   <properties>
+    <property name="name" value="grass"/>
+   </properties>
+  </tile>
+  <tile id="245">
+   <properties>
+    <property name="type" value="GrassCave"/>
+   </properties>
+  </tile>
+  <tile id="261">
+   <properties>
+    <property name="type" value="SandCave"/>
+   </properties>
+  </tile>
+  <tile id="275">
+   <properties>
+    <property name="name" value="castle-door"/>
+   </properties>
+  </tile>
+ </tileset>
+ <layer name="Grass and Water" width="15" height="15">
+  <data encoding="base64">
+   EgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAAGsAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAawAAAGsAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAABrAAAAawAAAGsAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAAGsAAABrAAAAawAAAGsAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAA
+  </data>
+ </layer>
+ <layer name="Tile Layer 1" width="15" height="15">
+  <data encoding="base64">
+   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8wAAAPQAAAD1AAAAAAAAAPYAAAD3AAAA+AAAAPkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwEAAAQBAAAFAQAAAAAAAAAAAAAHAQAACAEAAAkBAAD3AAAA+AAAAPkAAAAAAAAAAAAAAAAAAAAAAAAAEwEAABQBAAAVAQAAAAAAAAAAAAAXAQAAGAEAABkBAAAHAQAACAEAAAkBAAD2AAAAAAAAAAAAAAAAAAAAAAAAACQBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXAQAAGAEAABkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPYAAAD2AAAAAAAAAAAAAAAAAAAAAAAAACQBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMBAAA1AQAANQEAADQBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQBAAAAAAAAAAAAAAAAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQgAAACMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAAAAIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAiAAAAIgAAACMAAAAAAAAAAAAAAAAAAAAAAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+  </data>
+ </layer>
+ <layer name="Tile Layer 2" width="15" height="15">
+  <data encoding="base64">
+   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASgEAQEoBAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOgEAQDoBAMAAAAAAKgEAYDoBAGBKAQBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgEAQCoBAMAAAAAASgEAoDoBAKAqAQCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgEAACoBAIAAAAAAKgEAYDoBAGBKAQBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOgEAADoBAIAAAAAASgEAoDoBAKAqAQCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASgEAAEoBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+  </data>
+ </layer>
+ <objectgroup name="Object Layer 1" width="15" height="15">
+  <object name="Castle" x="192" y="16">
+   <properties>
+    <property name="Population" value="234"/>
+   </properties>
+   <polygon points="0,0 0,32 48,32 48,0"/>
+  </object>
+  <object name="Towers Area" x="0" y="192">
+   <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/>
+  </object>
+  <object name="Coastline" x="0" y="208">
+   <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/>
+  </object>
+  <object name="GenericObject" x="96" y="64" width="32" height="32"/>
+  <object name="SandCave" gid="262" x="64" y="80"/>
+  <object name="Road" x="224" y="48">
+   <properties>
+    <property name="MoveCost" value="2"/>
+   </properties>
+   <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/>
+  </object>
+ </objectgroup>
+</map>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/formosa-csv.tmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16">
+ <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16">
+  <image source="16x16-overworld.png" width="256" height="336"/>
+  <tile id="17">
+   <properties>
+    <property name="name" value="grass"/>
+   </properties>
+  </tile>
+  <tile id="245">
+   <properties>
+    <property name="type" value="GrassCave"/>
+   </properties>
+  </tile>
+  <tile id="261">
+   <properties>
+    <property name="type" value="SandCave"/>
+   </properties>
+  </tile>
+  <tile id="275">
+   <properties>
+    <property name="name" value="castle-door"/>
+   </properties>
+  </tile>
+ </tileset>
+ <layer name="Grass and Water" width="15" height="15">
+  <data encoding="csv">
+18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,
+18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,
+18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,
+18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,
+18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,
+18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,
+18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,
+18,18,18,18,18,18,18,18,18,18,18,18,18,18,107,
+18,18,18,18,18,18,18,18,18,18,18,18,18,107,107,
+18,18,18,18,18,18,18,18,18,18,18,18,107,107,107,
+18,18,18,18,18,18,18,18,18,18,18,107,107,107,107,
+18,18,18,18,18,18,18,18,18,18,107,107,107,107,107,
+18,18,18,18,18,18,18,107,107,107,107,107,107,107,107,
+107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,
+107,107,107,107,107,107,107,107,107,107,107,107,107,107,107
+</data>
+ </layer>
+ <layer name="Tile Layer 1" width="15" height="15">
+  <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,243,244,245,
+0,246,247,248,249,0,0,0,0,0,0,0,259,260,261,
+0,0,263,264,265,247,248,249,0,0,0,0,275,276,277,
+0,0,279,280,281,263,264,265,246,0,0,0,0,2147483940,0,
+0,0,0,0,0,279,280,281,0,0,0,0,0,2147483940,0,
+0,0,0,0,0,0,0,246,246,0,0,0,0,2147483940,0,
+0,0,0,0,0,0,0,0,0,0,323,309,309,2147483956,0,
+0,0,0,0,0,0,0,0,0,0,308,0,0,0,66,
+0,0,0,0,0,0,0,0,0,0,0,0,0,66,35,
+0,0,0,0,0,0,0,0,0,0,0,0,66,35,0,
+0,0,0,0,0,0,0,0,0,0,0,66,35,0,0,
+0,0,0,0,0,0,0,0,0,0,66,35,0,0,0,
+0,0,0,0,0,0,0,66,34,34,35,0,0,0,0,
+34,34,34,34,34,34,34,35,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer name="Tile Layer 2" width="15" height="15">
+  <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1073742154,3221225802,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1073742138,3221225786,0,1610613034,1610613050,1610613066,0,0,0,0,0,0,0,0,0,
+1073742122,3221225770,0,2684354890,2684354874,2684354858,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+298,2147483946,0,1610613034,1610613050,1610613066,0,0,0,0,0,0,0,0,0,
+314,2147483962,0,2684354890,2684354874,2684354858,0,0,0,0,0,0,0,0,0,
+330,2147483978,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <objectgroup name="Object Layer 1" width="15" height="15">
+  <object name="Castle" x="192" y="16">
+   <properties>
+    <property name="Population" value="234"/>
+   </properties>
+   <polygon points="0,0 0,32 48,32 48,0"/>
+  </object>
+  <object name="Towers Area" x="0" y="192">
+   <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/>
+  </object>
+  <object name="Coastline" x="0" y="208">
+   <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/>
+  </object>
+  <object name="GenericObject" x="96" y="64" width="32" height="32"/>
+  <object name="SandCave" gid="262" x="64" y="80"/>
+  <object name="Road" x="224" y="48">
+   <properties>
+    <property name="MoveCost" value="2"/>
+   </properties>
+   <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/>
+  </object>
+ </objectgroup>
+</map>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/formosa-xml.tmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,735 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16">
+ <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16">
+  <image source="16x16-overworld.png" width="256" height="336"/>
+  <tile id="17">
+   <properties>
+    <property name="name" value="grass"/>
+   </properties>
+  </tile>
+  <tile id="245">
+   <properties>
+    <property name="type" value="GrassCave"/>
+   </properties>
+  </tile>
+  <tile id="261">
+   <properties>
+    <property name="type" value="SandCave"/>
+   </properties>
+  </tile>
+  <tile id="275">
+   <properties>
+    <property name="name" value="castle-door"/>
+   </properties>
+  </tile>
+ </tileset>
+ <layer name="Grass and Water" width="15" height="15">
+  <data>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="107"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="18"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+   <tile gid="107"/>
+  </data>
+ </layer>
+ <layer name="Tile Layer 1" width="15" height="15">
+  <data>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="243"/>
+   <tile gid="244"/>
+   <tile gid="245"/>
+   <tile gid="0"/>
+   <tile gid="246"/>
+   <tile gid="247"/>
+   <tile gid="248"/>
+   <tile gid="249"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="259"/>
+   <tile gid="260"/>
+   <tile gid="261"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="263"/>
+   <tile gid="264"/>
+   <tile gid="265"/>
+   <tile gid="247"/>
+   <tile gid="248"/>
+   <tile gid="249"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="275"/>
+   <tile gid="276"/>
+   <tile gid="277"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="279"/>
+   <tile gid="280"/>
+   <tile gid="281"/>
+   <tile gid="263"/>
+   <tile gid="264"/>
+   <tile gid="265"/>
+   <tile gid="246"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="2147483940"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="279"/>
+   <tile gid="280"/>
+   <tile gid="281"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="2147483940"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="246"/>
+   <tile gid="246"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="2147483940"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="323"/>
+   <tile gid="309"/>
+   <tile gid="309"/>
+   <tile gid="2147483956"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="308"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="66"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="66"/>
+   <tile gid="35"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="66"/>
+   <tile gid="35"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="66"/>
+   <tile gid="35"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="66"/>
+   <tile gid="35"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="66"/>
+   <tile gid="34"/>
+   <tile gid="34"/>
+   <tile gid="35"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="34"/>
+   <tile gid="34"/>
+   <tile gid="34"/>
+   <tile gid="34"/>
+   <tile gid="34"/>
+   <tile gid="34"/>
+   <tile gid="34"/>
+   <tile gid="35"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+  </data>
+ </layer>
+ <layer name="Tile Layer 2" width="15" height="15">
+  <data>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="1073742154"/>
+   <tile gid="3221225802"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="1073742138"/>
+   <tile gid="3221225786"/>
+   <tile gid="0"/>
+   <tile gid="1610613034"/>
+   <tile gid="1610613050"/>
+   <tile gid="1610613066"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="1073742122"/>
+   <tile gid="3221225770"/>
+   <tile gid="0"/>
+   <tile gid="2684354890"/>
+   <tile gid="2684354874"/>
+   <tile gid="2684354858"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="298"/>
+   <tile gid="2147483946"/>
+   <tile gid="0"/>
+   <tile gid="1610613034"/>
+   <tile gid="1610613050"/>
+   <tile gid="1610613066"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="314"/>
+   <tile gid="2147483962"/>
+   <tile gid="0"/>
+   <tile gid="2684354890"/>
+   <tile gid="2684354874"/>
+   <tile gid="2684354858"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="330"/>
+   <tile gid="2147483978"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+   <tile gid="0"/>
+  </data>
+ </layer>
+ <objectgroup name="Object Layer 1" width="15" height="15">
+  <object name="Castle" x="192" y="16">
+   <properties>
+    <property name="Population" value="234"/>
+   </properties>
+   <polygon points="0,0 0,32 48,32 48,0"/>
+  </object>
+  <object name="Towers Area" x="0" y="192">
+   <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/>
+  </object>
+  <object name="Coastline" x="0" y="208">
+   <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/>
+  </object>
+  <object name="GenericObject" x="96" y="64" width="32" height="32"/>
+  <object name="SandCave" gid="262" x="64" y="80"/>
+  <object name="Road" x="224" y="48">
+   <properties>
+    <property name="MoveCost" value="2"/>
+   </properties>
+   <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/>
+  </object>
+ </objectgroup>
+</map>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/frnknstn.tmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="10" height="10" tilewidth="32" tileheight="32">
+ <tileset firstgid="1" name="tiles" tilewidth="32" tileheight="32">
+  <image source="tiles.png" width="64" height="64"/>
+  <tile id="1">
+   <properties>
+    <property name="color" value="blue"/>
+   </properties>
+  </tile>
+ </tileset>
+ <layer name="Tile Layer 1" width="10" height="10">
+  <data encoding="base64" compression="zlib">
+   eJyNkMEKADAIQh32/9+8HQqk2djhEWWYRABxYBJSla5Pfc1W0n1U+8H5l6a9y07MOZx/3+1ZaHyJO6+79fqVzjfbTAEP
+  </data>
+ </layer>
+ <objectgroup name="Object Layer 1" width="10" height="10">
+  <object gid="2" x="96" y="128"/>
+ </objectgroup>
+</map>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/pytmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,1 @@
+../pytmx
\ No newline at end of file
Binary file 002/pytmx_docs/tests/sewer_tileset.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/sewers.tmx	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="50" height="50" tilewidth="24" tileheight="24">
+ <tileset firstgid="1" name="sewer_tileset" tilewidth="24" tileheight="24">
+  <image source="sewer_tileset.png" trans="ff00ff" width="192" height="217"/>
+ </tileset>
+ <layer name="Bottom" width="50" height="50">
+  <data encoding="base64" compression="zlib">
+   eJzt19kKwjAQBdDim0sFqwguL3Vf/sP//ySnkIFhSGrSdEnxPhyQxqJ32kySPMuyJTkZC5KLa2dyNEo1Lsds3wkl/4f+7V/0/f+Y40Je5GpUn2+J5NiQCdmO/HlMyYzMI3JwLUJx7drIsSIFWUfk4FrY3GvGuHb8vr4jcmhNcnAtpIflmmar3VA5nqaWMUKeQ1d9dyht59iRPTmMPEdpua8Put/Frh88P5q84zFkv/NZP2TOlOaH7Hc+64fMmdL8cPVbV9+tcn5MzpTmR0gv12uDbQ4MNT8AIA73Uq3v3hqLe2ldTx4D21k1tf2ubw59Vk1tX+KbQ59VXfuSlMn9SJHV70tS5jqrYu8BAAAAAAAAAABd+wIHfQq1
+  </data>
+ </layer>
+ <layer name="Top" width="50" height="50" opacity="0.49">
+  <data encoding="base64" compression="zlib">
+   eJzt1jsKgDAQQEELtVKvYuGvEDvvfya3MBeQQAzMwCPdsum2af5lL71AJv7xL7X+Y46WtzXayq7z2RGd0f2+V6a5bdRFfaZ5pQzRGE2lFwEAoArpDk7Veg+nOzjlHgYAAAAAIIcHvboDlQ==
+  </data>
+ </layer>
+</map>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/002/pytmx_docs/tests/test.py	Sun Jun 17 16:34:43 2012 +0200
@@ -0,0 +1,91 @@
+"""
+This is tested on pygame 1.9 and python 2.7.
+This will not work on python 3.  Don't ask either.  I will say 'no'.
+bitcraft (leif dot theden at gmail.com)
+
+Rendering demo for the TMXLoader.  This simply shows that the loader works.
+If you need a rendering library that will handle large maps and scrolling, you
+can check out my lib2d project at pygame.org.  Have fun!
+"""
+
+class TiledRenderer(object):
+    """
+    Super simple way to render a tiled map
+    """
+
+    def __init__(self, filename):
+        from pytmx import tmxloader
+        self.tiledmap = tmxloader.load_pygame(filename, pixelalpha=True)
+   
+ 
+    def render(self, surface):
+        # not going for effeciency here
+        # for demonstration purposes only
+
+        tw = self.tiledmap.tilewidth
+        th = self.tiledmap.tileheight
+        gt = self.tiledmap.getTileImage
+
+        for l in xrange(0, len(self.tiledmap.tilelayers)):
+            for y in xrange(0, self.tiledmap.height):
+                for x in xrange(0, self.tiledmap.width):
+                    tile = gt(x, y, l)
+                    if tile: surface.blit(tile, (x*tw, y*th))
+
+        for og in self.tiledmap.objectgroups:
+            for o in og:
+                if hasattr(o, 'points'):
+                    points = [ (i[0]+o.x, i[1]+o.y) for i in o.points ]
+                    pygame.draw.lines(surface, (255,128,128), o.closed, points, 2)
+                else:
+                    pygame.draw.rect(surface, (255,128,128),
+                                    (o.x, o.y, o.width, o.height), 2)
+
+import pygame
+from pygame.locals import *
+import math, glob
+from itertools import product
+
+
+pygame.init()
+pygame.font.init()
+screen = pygame.display.set_mode((480, 480))
+pygame.display.set_caption('TMXLoader Test')
+
+
+def simpleTest(filename):
+    screen_buf = pygame.Surface((240, 240))
+    screen_buf.fill((0,128,255))
+    formosa = TiledRenderer(filename)
+    formosa.render(screen_buf)
+    pygame.transform.scale(screen_buf, screen.get_size(), screen)
+    f = pygame.font.Font(pygame.font.get_default_font(), 20)
+    i = f.render(filename, 1, (180,180,0))
+    screen.blit(i, (0,0))
+    pygame.display.flip()
+
+    print "Objects in map:"
+    for o in formosa.tiledmap.getObjects():
+        print o
+        for k, v in o.__dict__.items():
+            print "  ", k, v
+
+    print "GID (tile) properties:"
+    for k, v in formosa.tiledmap.tile_properties.items():
+        print "  ", k, v
+
+    run = True
+    while run:
+        try:
+            event = pygame.event.wait()
+            if (event.type == QUIT) or (event.type == KEYDOWN): run = False
+
+        except KeyboardInterrupt:
+            run = False
+
+
+for filename in glob.glob('*.tmx'):
+    print "testing", filename
+    simpleTest(filename)
+
+pygame.quit()
Binary file 002/pytmx_docs/tests/tiles.png has changed
Binary file 002/pytmx_docs/tests/tmw_desert_spacing.png has changed
Binary file 002/spritesheet.png has changed
Binary file 002/tilesheet.png has changed