diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2011, cra
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of cra nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/htiled.cabal b/htiled.cabal
new file mode 100644
--- /dev/null
+++ b/htiled.cabal
@@ -0,0 +1,24 @@
+Name:                htiled
+Version:             0.0.2
+Synopsis:            Import from the Tiled map editor.
+Description:         Import maps from the .tmx map format generated by Tiled,
+                     <http://www.mapeditor.org>.
+License:             BSD3
+License-file:        LICENSE
+Author:              Christian Rødli Amble
+Maintainer:          cra+code@cra.no
+Category:            Game
+Build-type:          Simple
+Cabal-version:       >=1.6
+
+Source-Repository head
+  Type:              git
+  Location:          git://github.com/chrra/htiled.git
+
+Library
+  Hs-Source-Dirs:    src
+  Exposed-Modules:   Data.Tiled, Data.Tiled.Types, Data.Tiled.Load
+  Build-depends:       base ==0.4.*, zlib ==0.5.*, bytestring ==0.9.*
+                     , hxt >9.1 && <9.3, base64-bytestring ==0.1.*
+                     , containers ==0.4.*, filepath
+  Ghc-Options:       -Wall
diff --git a/src/Data/Tiled.hs b/src/Data/Tiled.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Tiled.hs
@@ -0,0 +1,6 @@
+module Data.Tiled
+    ( module X
+    ) where
+
+import Data.Tiled.Load as X
+import Data.Tiled.Types as X
diff --git a/src/Data/Tiled/Load.hs b/src/Data/Tiled/Load.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Tiled/Load.hs
@@ -0,0 +1,153 @@
+{-# LANGUAGE Arrows, UnicodeSyntax, RecordWildCards #-}
+module Data.Tiled.Load (loadMapFile) where
+
+import Prelude hiding ((.), id)
+import Control.Category ((.), id)
+import Data.Bits (testBit, clearBit)
+import qualified Data.ByteString.Base64 as B64
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.ByteString.Lazy.Char8 as LBS
+import Data.Char (digitToInt)
+import Data.List (sort)
+import Data.Map (fromDistinctAscList, Map)
+import Data.Maybe (listToMaybe, fromMaybe, isNothing)
+import Data.Word (Word32)
+
+import qualified Codec.Compression.GZip as GZip
+import qualified Codec.Compression.Zlib as Zlib
+import Text.XML.HXT.Core
+
+import Data.Tiled.Types
+
+-- | Load a map file.
+loadMapFile ∷ FilePath → IO TiledMap
+loadMapFile fp = head `fmap` runX (
+        configSysVars [withValidate no, withWarnings yes]
+    >>> readDocument [] fp
+    >>> getChildren >>> isElem
+    >>> doMap fp)
+
+properties ∷ IOSArrow XmlTree Properties
+properties = listA $ getChildren >>> isElem >>> hasName "properties"
+            >>> getChildren >>> isElem >>> hasName "property"
+            >>> getAttrValue "name" &&& getAttrValue "value"
+
+getAttrR ∷ (Read α, Num α) ⇒ String → IOSArrow XmlTree α
+getAttrR a = arr read . getAttrValue0 a
+
+doMap ∷ FilePath → IOSArrow XmlTree TiledMap
+doMap mapPath = proc m → do
+    mapOrientation ← arr (\x → case x of "orthogonal" → Orthogonal
+                                         "isometric" → Isometric
+                                         _ → error "unsupported orientation")
+                     . getAttrValue "orientation" ⤙ m
+    mapWidth       ← getAttrR "width"      ⤙ m
+    mapHeight      ← getAttrR "height"     ⤙ m
+    mapTileWidth   ← getAttrR "tilewidth"  ⤙ m
+    mapTileHeight  ← getAttrR "tileheight" ⤙ m
+    mapProperties  ← properties            ⤙ m
+    mapTilesets    ← tilesets              ⤙ m
+    mapLayers      ← layers                ⤙ (m, (mapWidth, mapHeight))
+    returnA        ⤙ TiledMap {..}
+
+layers ∷ IOSArrow (XmlTree, (Int, Int)) [Layer]
+layers = listA (first (getChildren >>> isElem) >>> doObjectGroup <+> doLayer)
+  where
+    doObjectGroup = arr fst >>> hasName "objectgroup" >>> id &&& (listA object >>> arr Right) >>> common
+
+    object = getChildren >>> isElem >>> hasName "object"
+         >>> proc obj → do
+        objectName     ← arr listToMaybe . listA (getAttrValue "name") ⤙ obj
+        objectType     ← arr listToMaybe . listA (getAttrValue "type") ⤙ obj
+        objectX        ← getAttrR "x"                                  ⤙ obj
+        objectY        ← getAttrR "y"                                  ⤙ obj
+        objectWidth    ← arr listToMaybe . listA (getAttrR "width")    ⤙ obj
+        objectHeight   ← arr listToMaybe . listA (getAttrR "height")   ⤙ obj
+        objectGid      ← arr listToMaybe . listA (getAttrR "gid")      ⤙ obj
+        objectPolygon  ← arr listToMaybe . polygon                     ⤙ obj
+        objectPolyline ← arr listToMaybe . polyline                    ⤙ obj
+        objectProperties ← properties                                  ⤙ obj
+        returnA      ⤙ Object {..}
+
+    polygon ∷ IOSArrow XmlTree [Polygon]
+    polygon = listA $ getChildren >>> isElem >>> hasName "polygon"
+          >>> getAttrValue "points" >>> arr (Polygon . points)
+    polyline ∷ IOSArrow XmlTree [Polyline]
+    polyline = listA $ getChildren >>> isElem >>> hasName "polyline"
+          >>> getAttrValue "points" >>> arr (Polyline . points)
+
+    points :: String → [(Int, Int)]
+    points s = (x, y):if null rest then [] else points rest
+        where (p, rest) = drop 1 `fmap` break (==' ') s
+              (x', y') = drop 1 `fmap` break (==',') p
+              x = read x'
+              y = read y'
+
+    doLayer = first (hasName "layer") >>> arr fst &&& (doData >>> arr Left) >>> common
+
+    doData = first (getChildren >>> isElem >>> hasName "data")
+         >>> proc (dat, (w, h)) → do
+                encoding    ← getAttrValue "encoding"        ⤙ dat
+                compression ← getAttrValue "compression"     ⤙ dat
+                text        ← getText . isText . getChildren ⤙ dat
+                returnA ⤙ dataToTiles w h encoding compression text
+
+    -- Width → Height → Encoding → Compression → Data → [Tile]
+    dataToTiles ∷ Int → Int → String → String → String → Map (Int, Int) Tile
+    dataToTiles w h "base64" "gzip" = toMap w h . base64 GZip.decompress
+    dataToTiles w h "base64" "zlib" = toMap w h . base64 Zlib.decompress
+    dataToTiles _ _ _ _ = error "unsupported tile data format, only base64 and \
+                                \gzip/zlib is supported at the moment."
+
+    toMap w h = fromDistinctAscList . sort . filter (\(_, x) → tileGid x /= 0)
+                . zip [(x, y) | y ← [0..h-1], x ← [0..w-1]]
+
+    base64 f = bytesToTiles . LBS.unpack . f . LBS.fromChunks
+                            . (:[]) . B64.decodeLenient . BS.pack
+
+    bytesToTiles (a:b:c:d:xs) = Tile { .. } : bytesToTiles xs
+      where n = f a + f b * 256 + f c * 65536 + f d * 16777216
+            f = fromIntegral . fromEnum ∷ Char → Word32
+            tileGid = n `clearBit` 30 `clearBit` 31
+            tileIsVFlipped = n `testBit` 30
+            tileIsHFlipped = n `testBit` 31
+    bytesToTiles [] = []
+    bytesToTiles _ = error "number of bytes not a multiple of 4."
+
+    common = proc (l, x) → do
+        layerName       ← getAttrValue "name"              ⤙ l
+        layerOpacity    ← arr (fromMaybe 1 . listToMaybe)
+                          . listA (getAttrR "opacity")     ⤙ l
+        layerIsVisible  ← arr (isNothing . listToMaybe)
+                          . listA (getAttrValue "visible") ⤙ l
+        layerProperties ← properties                      ⤙ l
+        returnA ⤙ case x of Left  layerData    → Layer {..}
+                            Right layerObjects → ObjectLayer {..}
+
+
+tilesets ∷ IOSArrow XmlTree [Tileset]
+tilesets = listA $ getChildren >>> isElem >>> hasName "tileset"
+         >>> proc ts → do
+              tsName        ← getAttrValue "name"     ⤙ ts
+              tsInitialGid  ← getAttrR "firstgid"     ⤙ ts
+              tsTileWidth   ← getAttrR "tilewidth"    ⤙ ts
+              tsTileHeight  ← getAttrR "tileheight"   ⤙ ts
+              tsImages      ← images                  ⤙ ts
+              tsTileProperties ← listA tileProperties ⤙ ts
+              returnA ⤙ Tileset {..}
+  where tileProperties ∷ IOSArrow XmlTree (Word32, Properties)
+        tileProperties = getChildren >>> isElem >>> hasName "tile"
+                     >>> getAttrR "id" &&& properties
+
+        images = listA (getChildren >>> isElem >>> hasName "image" >>>
+                         proc image → do
+                              iSource ← getAttrValue "source"   ⤙ image
+                              iTrans  ← arr (fmap colorToTriplet . listToMaybe)
+                                            . listA (getAttrValue0 "trans") ⤙ image
+                              iWidth  ← getAttrR "width"        ⤙ image
+                              iHeight ← getAttrR "height"       ⤙ image
+                              returnA ⤙ Image {..})
+
+        colorToTriplet x = (h x, h $ drop 2 x, h $ drop 4 x)
+          where h (y:z:_) = fromIntegral $ digitToInt y * 16 + digitToInt z
+                h _ = error "invalid color in an <image ...> somewhere."
diff --git a/src/Data/Tiled/Types.hs b/src/Data/Tiled/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Tiled/Types.hs
@@ -0,0 +1,93 @@
+{-# LANGUAGE UnicodeSyntax, RecordWildCards #-}
+module Data.Tiled.Types where
+
+import Data.Map (Map)
+import Data.Word (Word8, Word32)
+
+-- | Orientations.
+data MapOrientation = Orthogonal | Isometric deriving (Show, Eq)
+
+-- | Properties.
+type Properties = [(String, String)]
+
+-- | A tiled map.
+data TiledMap = TiledMap
+         { mapPath             ∷ FilePath -- ^ The file path of the map file.
+         , mapOrientation      ∷ MapOrientation
+         , mapWidth, mapHeight ∷ Int
+         , mapTileWidth        ∷ Int
+         , mapTileHeight       ∷ Int
+         , mapProperties       ∷ Properties
+         , mapTilesets         ∷ [Tileset]
+         , mapLayers           ∷ [Layer]
+         } deriving (Show, Eq)
+
+-- | A set of tiles that can be used.
+data Tileset = Tileset
+             { tsName                    ∷ String
+             , tsInitialGid              ∷ Word32
+             , tsTileWidth, tsTileHeight ∷ Int
+             , tsImages                  ∷ [Image] -- ^ Multiple images not
+                                                   -- yet supported in tiled.
+             , tsTileProperties          ∷ [(Word32, Properties)]
+             } deriving (Show, Eq)
+
+-- | An image containing tiles.
+data Image = Image
+           { iSource         ∷ FilePath
+           , iTrans          ∷ Maybe (Word8, Word8, Word8)
+           , iWidth, iHeight ∷ Int
+           } deriving (Show, Eq)
+
+-- | An object, usable for stuff not repetitively aligned on a grid.
+data Object = Object
+            { objectName                ∷ Maybe String
+            , objectType                ∷ Maybe String
+            , objectProperties          ∷ Properties
+            , objectX, objectY          ∷ Int
+            , objectWidth, objectHeight ∷ Maybe Int
+            , objectGid                 ∷ Maybe Word32
+            , objectPolygon             ∷ Maybe Polygon
+            , objectPolyline            ∷ Maybe Polyline
+            } deriving (Show, Eq)
+
+-- | A polygon.
+data Polygon = Polygon [(Int, Int)] deriving (Show, Eq)
+
+-- | A polyline.
+data Polyline = Polyline [(Int, Int)] deriving (Show, Eq)
+
+-- | Either a tile layer or an object layer.
+data Layer = Layer
+           { layerName       ∷ String
+           , layerOpacity    ∷ Float
+           , layerIsVisible  ∷ Bool
+           , layerProperties ∷ Properties
+           , layerData       ∷ Map (Int, Int) Tile
+           }
+           | ObjectLayer
+           { layerName       ∷ String
+           , layerOpacity    ∷ Float
+           , layerIsVisible  ∷ Bool
+           , layerProperties ∷ Properties
+           , layerObjects    ∷ [Object]
+           } deriving Eq
+
+-- | A single tile as is stored in a layer.
+data Tile = Tile { tileGid                        ∷ Word32
+                 , tileIsVFlipped, tileIsHFlipped ∷ Bool
+                 } deriving (Show, Eq, Ord)
+
+
+instance Show Layer where
+    show Layer {..} = "Layer { layerName = " ++ show layerName ++
+                            ", layerOpacity = " ++ show layerOpacity ++
+                            ", layerIsVisible = " ++ show layerIsVisible ++
+                            ", layerProperties = " ++ show layerProperties ++
+                            ", layerData = \"...\" }"
+    show ObjectLayer {..} = "ObjectLayer { layerName = " ++ show layerName ++
+                                        ", layerOpacity = " ++ show layerOpacity ++
+                                        ", layerIsVisible = " ++ show layerIsVisible ++
+                                        ", layerProperties = " ++ show layerProperties ++
+                                        ", layerObjects = " ++ show layerObjects ++ " }"
+
