codders / gp3

a game

This URL has Read+Write access

gp3 / src / BPackReader.hs
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 1 {-# LANGUAGE CPP #-}
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 2 module BPackReader
8f1112c8 » codders 2009-01-03 Parse map file separately f... 3 (parseImageFile,
4 parseMapFile,
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 5 ParsedImage,
6 gliphs,
7 palette,
4217b326 » codders 2009-01-03 Drawing... err... something 8 gliphSize,
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 9 PaletteEntry,
10 Gliph,
11 gliphData,
12 gliphWidth,
13 gliphHeight,
14 gliphDepth,
044b4193 » codders 2009-01-03 Fixed tile mapping by intro... 15 blankGliph,
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 16 red,
17 green,
8f1112c8 » codders 2009-01-03 Parse map file separately f... 18 blue,
19 ParsedTileMap,
20 tileMap,
21 tilesHigh,
22 tilesAcross)
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 23 where
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 24
25 import Text.ParserCombinators.Parsec
26 import qualified Data.ByteString.Lazy as L
27 import qualified Data.ByteString.Lazy.Char8 as L8
b2069a02 » codders 2008-12-26 Added palette parsing 28 import qualified Data.ByteString.Internal as LI
29 import qualified Data.Word as DW
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 30 import Data.List
b2069a02 » codders 2008-12-26 Added palette parsing 31 import Data.Bits
8f1112c8 » codders 2009-01-03 Parse map file separately f... 32 import Data.Maybe
04d66b53 » codders 2008-12-10 Added a null decompressor 33 import qualified GHC.Word as W
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 34 import Control.Exception (bracket, handle)
04d66b53 » codders 2008-12-10 Added a null decompressor 35 import Control.Monad
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 36 import System.IO
b2069a02 » codders 2008-12-26 Added palette parsing 37 import Text.Printf
8f1112c8 » codders 2009-01-03 Parse map file separately f... 38 import Debug.Trace
39
40 -- For Level Maps
41 #define LEVELMAP_OFFSET_BYTES 130
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 42
8f1112c8 » codders 2009-01-03 Parse map file separately f... 43 -- For Tile Sets
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 44 #define BITMAP_OFFSET_BYTES 54
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 45 #define TILE_DIMENSION_PIXELS 16
46 #define TILE_BITPLANE_BYTES (TILE_DIMENSION_PIXELS * TILE_DIMENSION_PIXELS `div` 8)
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 47 #define BITPLANES 4
48 #define TILE_DATA_BYTES (TILE_BITPLANE_BYTES * BITPLANES)
4217b326 » codders 2009-01-03 Drawing... err... something 49 #define IMAGE_HEIGHT_TILES 63
50 #define IMAGE_WIDTH_TILES 21
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 51
8f1112c8 » codders 2009-01-03 Parse map file separately f... 52 data BPackedFile = BPF {
04d66b53 » codders 2008-12-10 Added a null decompressor 53 bit8Marker :: W.Word8,
54 bit16Marker :: W.Word8,
55 size :: Integer,
8f1112c8 » codders 2009-01-03 Parse map file separately f... 56 fileData :: L.ByteString
04d66b53 » codders 2008-12-10 Added a null decompressor 57 }
58
70c4452d » codders 2009-01-04 Added support for reading u... 59 data FileType = CompressedFile
60 | UncompressedFile
61
8f1112c8 » codders 2009-01-03 Parse map file separately f... 62 data UnpackedFile = UPF {
63 rawFileData :: L.ByteString
04d66b53 » codders 2008-12-10 Added a null decompressor 64 }
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 65
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 66 data Gliph = GL {
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 67 gliphData :: L.ByteString,
68 gliphWidth :: Int,
69 gliphHeight :: Int,
70 gliphDepth :: Int
d5cc9c9c » codders 2009-01-04 Fixed up rendering by flipp... 71 }
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 72
b2069a02 » codders 2008-12-26 Added palette parsing 73 data PaletteEntry = PE {
74 red :: Integer,
75 green :: Integer,
76 blue :: Integer,
77 index :: Integer
78 } deriving (Show)
79
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 80 data ParsedImage = PI {
81 gliphs :: [Gliph],
4217b326 » codders 2009-01-03 Drawing... err... something 82 palette :: [PaletteEntry],
83 gliphSize :: Int
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 84 }
85
8f1112c8 » codders 2009-01-03 Parse map file separately f... 86 data ParsedTileMap = PTM {
87 tileMap :: [W.Word8],
88 tilesAcross :: Int,
89 tilesHigh :: Int
90 }
91
d5cc9c9c » codders 2009-01-04 Fixed up rendering by flipp... 92 instance Show Gliph where
93 show g = "Gliph:\n"++(dumpImage (gliphData g) 0)
94
8f1112c8 » codders 2009-01-03 Parse map file separately f... 95 instance Show ParsedTileMap where
96 show tm = "Parsed Tile map has " ++ show (length $ tileMap tm) ++ " tiles in the map"
97
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 98 instance Show ParsedImage where
8bb6ac73 » codders 2009-01-04 Removed verbose debug 99 show im = "Parsed Image has " ++ show (length $ gliphs im) ++ " shapes and " ++ show (length $ palette im) ++ " colours."
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 100
8f1112c8 » codders 2009-01-03 Parse map file separately f... 101 instance Show BPackedFile where
102 show im = "Packed image, compressed size: " ++ show (L.length $ fileData im) ++ " (decompressed: " ++ show (size im) ++ ") with markers " ++ show (bit8Marker im) ++ " and " ++ show (bit16Marker im)
04d66b53 » codders 2008-12-10 Added a null decompressor 103
8f1112c8 » codders 2009-01-03 Parse map file separately f... 104 instance Show UnpackedFile where
105 show im = "Unpacked image, size: " ++ show (L.length $ rawFileData im)
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 106
107 matchHeader :: L.ByteString -> Maybe L.ByteString
04d66b53 » codders 2008-12-10 Added a null decompressor 108 matchHeader fileData = do (head, tail) <- getBytes 4 fileData
109 if (head == L8.pack("BPCK"))
110 then return tail
111 else (fail "failed")
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 112
113 getBytes :: Int -> L.ByteString -> Maybe (L.ByteString, L.ByteString)
114 getBytes n str = let count = fromIntegral n
115 both@(prefix,_) = L.splitAt count str
116 in if L.length prefix < count
117 then Nothing
118 else Just both
119
04d66b53 » codders 2008-12-10 Added a null decompressor 120 getByte :: L.ByteString -> Maybe (W.Word8, L.ByteString)
121 getByte = L.uncons
122
123 skipBytes :: Int -> L.ByteString -> Maybe L.ByteString
124 skipBytes n str = do if L.length prefix < count
125 then Nothing
126 else Just tail
127 where count = fromIntegral n
128 (prefix, tail) = L.splitAt count str
129
b2069a02 » codders 2008-12-26 Added palette parsing 130 decompressBytes :: L.ByteString -> W.Word8 -> W.Word8 -> Maybe L.ByteString
8f1112c8 » codders 2009-01-03 Parse map file separately f... 131 decompressBytes fileData bit8marker bit16marker =
132 case L.uncons fileData of
b2069a02 » codders 2008-12-26 Added palette parsing 133 Just (top, tail) ->
134 if (top == bit8marker)
135 then do (n, rest) <- getByte tail
136 (v, rest) <- getByte rest
137 result <- (decompressBytes rest bit8marker bit16marker)
138 return $ L.append (L.replicate (fromIntegral n) v) result
139 else if (top == bit16marker)
140 then do (n255, rest) <- getByte tail
141 (n, rest) <- getByte rest
142 (v, rest) <- getByte rest
143 result <- decompressBytes rest bit8marker bit16marker
144 return $ L.append (L.replicate ((fromIntegral n255) * 256 + (fromIntegral n)) v) result
145 else do result <- (decompressBytes tail bit8marker bit16marker)
146 return $ L.cons top result
147 Nothing -> Just L.empty
04d66b53 » codders 2008-12-10 Added a null decompressor 148
8f1112c8 » codders 2009-01-03 Parse map file separately f... 149 unpackAsFile :: BPackedFile -> Maybe UnpackedFile
150 unpackAsFile bpi = do decompressed <- decompressBytes (fileData bpi) (bit8Marker bpi) (bit16Marker bpi)
151 return $ UPF decompressed
04d66b53 » codders 2008-12-10 Added a null decompressor 152
70c4452d » codders 2009-01-04 Added support for reading u... 153 buildParsedImage :: L.ByteString -> FileType -> Maybe ParsedImage
154 buildParsedImage content filetype = do
155 paletteBytes <- paletteData content
156 let parsedPalette = parsePalette paletteBytes
157 shapes <- case filetype of CompressedFile -> loadCompressedShapes content
158 UncompressedFile -> loadUncompressedShapes content
159 return $ PI shapes parsedPalette TILE_DIMENSION_PIXELS
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 160
8f1112c8 » codders 2009-01-03 Parse map file separately f... 161 unpackData :: L.ByteString -> Maybe UnpackedFile
162 unpackData fileData = do content <- matchHeader fileData
04d66b53 » codders 2008-12-10 Added a null decompressor 163 (bit8, content) <- getByte content
164 (bit16, content) <- getByte content
165 content <- skipBytes 4 content
166 (size_255, content) <- getByte content
167 (size, content) <- getByte content
8f1112c8 » codders 2009-01-03 Parse map file separately f... 168 unpackAsFile $ BPF bit8 bit16 ((fromIntegral size_255) * 255 + (fromIntegral size)) content
169
70c4452d » codders 2009-01-04 Added support for reading u... 170 parseAsTileMap :: L.ByteString -> FileType -> Maybe ParsedTileMap
171 parseAsTileMap fileData filetype = do
172 tileArray <- loadTileMap fileData
d5cc9c9c » codders 2009-01-04 Fixed up rendering by flipp... 173 return $ PTM tileArray IMAGE_WIDTH_TILES IMAGE_HEIGHT_TILES
8f1112c8 » codders 2009-01-03 Parse map file separately f... 174
70c4452d » codders 2009-01-04 Added support for reading u... 175 parseCompressedFile :: Show a => (L.ByteString -> FileType -> Maybe a) -> String -> IO (Maybe a)
8f1112c8 » codders 2009-01-03 Parse map file separately f... 176 parseCompressedFile parseFunction fileName = do
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 177 putStrLn $ "Loading from " ++ fileName
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 178 handle (\e -> do putStrLn $ "Error loading file: " ++ (show e); return Nothing) $
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 179 bracket (openFile fileName ReadMode) hClose $ \h -> do
180 fileData <- L.hGetContents h
70c4452d » codders 2009-01-04 Added support for reading u... 181 let unpacked = unpackData fileData
182 let object = case unpacked of Just unpackedFileData -> parseFunction (rawFileData unpackedFileData) CompressedFile
183 Nothing -> parseFunction fileData UncompressedFile
8f1112c8 » codders 2009-01-03 Parse map file separately f... 184 case object of Just parsedObject -> putStrLn $ "Loaded: " ++ (show parsedObject)
185 Nothing -> error "Unable to parse file"
186 return object
187
188
189 parseImageFile :: String -> IO (Maybe ParsedImage)
70c4452d » codders 2009-01-04 Added support for reading u... 190 parseImageFile = parseCompressedFile buildParsedImage
8f1112c8 » codders 2009-01-03 Parse map file separately f... 191
192 parseMapFile :: String -> IO (Maybe ParsedTileMap)
193 parseMapFile = parseCompressedFile parseAsTileMap
194
195 dumpDecompressed :: String -> IO ()
196 dumpDecompressed fileName = do
197 putStrLn $ "Loading from " ++ fileName
198 handle (\e -> do putStrLn $ "Error loading file: " ++ (show e); return ()) $
199 bracket (openFile fileName ReadMode) hClose $ \h -> do
200 fileData <- L.hGetContents h
201 let image = fromJust $ unpackData fileData
d5cc9c9c » codders 2009-01-04 Fixed up rendering by flipp... 202 putStrLn $ dumpImage (rawFileData image) 0
8f1112c8 » codders 2009-01-03 Parse map file separately f... 203 return ()
204
309aa7d8 » codders 2008-12-10 Updated the GUI to render a... 205
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 206 -- Utility function for hexdumping
b2069a02 » codders 2008-12-26 Added palette parsing 207 asciiof :: W.Word8 -> String
208 asciiof x = if (x > 20 && x < 127)
209 then [(LI.w2c x)]
210 else "."
211
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 212 -- Generates the hex string corresponding to 16 bytes
d5cc9c9c » codders 2009-01-04 Fixed up rendering by flipp... 213 dumpLine :: L.ByteString -> String
214 dumpLine l = dumpRest "" "" l
b2069a02 » codders 2008-12-26 Added palette parsing 215 where dumpRest hex ascii l =
216 case (L.uncons l) of
217 Just (head, tail) -> dumpRest (hex ++ (printf "%02X " head)) (ascii ++ (asciiof head)) tail
218 Nothing -> hex ++ ascii
219
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 220 -- Hexdumps the image data
d5cc9c9c » codders 2009-01-04 Fixed up rendering by flipp... 221 dumpImage :: L.ByteString -> Int -> String
222 dumpImage imagedata off = printf "%08X " off ++ remainder
223 where remainder = case (getBytes 16 imagedata) of
224 Just (line, rest) -> dumpLine line ++ "\n" ++ dumpImage rest (off + 16)
225 Nothing -> ""
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 226
227 -- Reads the palette bytes from the end of the file
b2069a02 » codders 2008-12-26 Added palette parsing 228 paletteData :: L.ByteString -> Maybe L.ByteString
229 paletteData imdata = do (header, rest) <- getBytes (fromIntegral $ L.length imdata - 32) imdata
230 (result, tail) <- getBytes 30 rest
231 return result
232
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 233 -- Creates a new Palette entry by shifting red, green and blue nibbles from the value provided
b2069a02 » codders 2008-12-26 Added palette parsing 234 genColour :: Integer -> Integer -> PaletteEntry
235 genColour value index = PE ((value `shiftR` 8) * 16) (((value .&. 0xF0) `shiftR` 4) * 16) ((value .&. 0x0F) * 16) index
236
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 237 -- Takes 30 bytes of palette data, treating each pair of bytes as a short, and generates
238 -- the corresponding palette of 15 colours
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 239 parsePalette :: L.ByteString -> [PaletteEntry]
d5cc9c9c » codders 2009-01-04 Fixed up rendering by flipp... 240 parsePalette palElements = (map (\(a,b) -> genColour b a) $ zip [1..15] cvalues) ++ [PE 0 0 0 0]
b2069a02 » codders 2008-12-26 Added palette parsing 241 where odds = filter (odd . fst) $ map (\(a,b) -> (a, fromIntegral b)) tupList
242 evens = filter (even . fst) $ map (\(a,b) -> (a, (fromIntegral b)*256)) tupList
243 tupList = L.zip (L.pack [0..fromIntegral ((L.length palElements)-1)]) palElements
244 tidy = map snd
245 cvalues = zipWith (+) (tidy odds) (tidy evens)
246
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 247 -- Takes the pixel data from four bitplanes to create a tile
248 createGliph :: L.ByteString -> L.ByteString -> L.ByteString -> L.ByteString -> Maybe Gliph
249 createGliph b8 b4 b2 b1 = do bytes <- expandByteStreams b8 b4 b2 b1
ebe2a83f » codders 2009-01-03 Converted tiles into GDK pi... 250 return $ GL bytes TILE_DIMENSION_PIXELS TILE_DIMENSION_PIXELS BITPLANES
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 251
044b4193 » codders 2009-01-03 Fixed tile mapping by intro... 252 -- Generates an empty Gliph
253 blankGliph :: Gliph
254 blankGliph = let bytecount = TILE_DIMENSION_PIXELS * TILE_DIMENSION_PIXELS
255 in GL (L.replicate bytecount 0) TILE_DIMENSION_PIXELS TILE_DIMENSION_PIXELS BITPLANES
256
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 257 -- Takes 4 8-bit words and makes 8 4-bit words
258 expandByte :: W.Word8 -> W.Word8 -> W.Word8 -> W.Word8 -> L.ByteString
259 expandByte b8 b4 b2 b1 = L.pack $ map pixelise [0..7]
260 where pixelise i = (((b8 `shiftR` (7-i)) .&. 1) `shiftL` 3) .|.
261 (((b4 `shiftR` (7-i)) .&. 1) `shiftL` 2) .|.
262 (((b2 `shiftR` (7-i)) .&. 1) `shiftL` 1) .|.
263 ((b1 `shiftR` (7-i) .&. 1))
264
265 -- Combines the four incoming bitstreams to create a string of 4-bit words
266 expandByteStreams :: L.ByteString -> L.ByteString -> L.ByteString -> L.ByteString -> Maybe L.ByteString
267 expandByteStreams b8 b4 b2 b1 = do (byte8, b8rest) <- getByte b8
268 (byte4, b4rest) <- getByte b4
269 (byte2, b2rest) <- getByte b2
270 (byte1, b1rest) <- getByte b1
d5cc9c9c » codders 2009-01-04 Fixed up rendering by flipp... 271 let thisByte = expandByte byte1 byte2 byte4 byte8
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 272 case (expandByteStreams b8rest b4rest b2rest b1rest) of
273 Just bs -> Just (thisByte `L.append` bs)
274 Nothing -> Just thisByte
275
70c4452d » codders 2009-01-04 Added support for reading u... 276 -- Gets the number of shapes in an image data block
277 numberOfShapes :: L.ByteString -> Int
278 numberOfShapes imdata = fromIntegral $ ((L.length imdata) - BITMAP_OFFSET_BYTES - TILE_BITPLANE_BYTES) `quot` TILE_DATA_BYTES
279
280 -- In an uncompressed gliph stream, the bitplanes are interleaved pairs of bytes
281 readGliphStream :: L.ByteString -> Maybe [Gliph]
282 readGliphStream stream = blocksForBitplanes (pairs 0 sEights) (pairs 1 sEights) (pairs 2 sEights) (pairs 3 sEights)
283 where sEights = eights stream
284 eights astream | L.length astream > 8 = let (head, tail) = L.splitAt 8 astream
285 in (L.unpack head) : eights tail
286 | otherwise = [L.unpack astream]
287 pairs n (h:t) = (L.pack $ take 2 (drop (2*n) h)) `L.append` (pairs n t)
288 pairs _ [] = L.empty
289
290 -- Skip the head then parse the remaining bytes
291 loadUncompressedShapes :: L.ByteString -> Maybe [Gliph]
292 loadUncompressedShapes imdata = do
293 (head, rest) <- getBytes BITMAP_OFFSET_BYTES imdata
294 readGliphStream rest
295
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 296 -- Takes a list of tuples of bytes from four bitplanes and returns a series of tiles
297 blocksForBitplanes :: L.ByteString -> L.ByteString -> L.ByteString -> L.ByteString -> Maybe [Gliph]
298 blocksForBitplanes b8 b4 b2 b1 = do (tb8, b8rest) <- getBytes TILE_BITPLANE_BYTES b8
299 (tb4, b4rest) <- getBytes TILE_BITPLANE_BYTES b4
300 (tb2, b2rest) <- getBytes TILE_BITPLANE_BYTES b2
301 (tb1, b1rest) <- getBytes TILE_BITPLANE_BYTES b1
302 gliphData <- createGliph tb8 tb4 tb2 tb1
303 case (blocksForBitplanes b8rest b4rest b2rest b1rest) of
304 Just x -> Just (gliphData : x)
305 Nothing -> Just [gliphData]
306
307 -- Reads in the tile pixel data, turning 4 bitplanes of pixels into an array of tiles of
308 -- 4-bit mapped-palette values
70c4452d » codders 2009-01-04 Added support for reading u... 309 loadCompressedShapes :: L.ByteString -> Maybe [Gliph]
310 loadCompressedShapes imdata = do
311 (head, rest) <- getBytes BITMAP_OFFSET_BYTES imdata
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 312 (bitplane8, rest) <- getBytes bitplaneSeparationBytes rest
313 (bitplane4, rest) <- getBytes bitplaneSeparationBytes rest
314 (bitplane2, rest) <- getBytes bitplaneSeparationBytes rest
315 (bitplane1, rest) <- getBytes bitplaneSeparationBytes rest
316 blocksForBitplanes bitplane8 bitplane4 bitplane2 bitplane1
70c4452d » codders 2009-01-04 Added support for reading u... 317 where bitplaneSeparationBytes = (numberOfShapes imdata) * TILE_BITPLANE_BYTES
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 318
319 -- Dumps a list of colours
b2069a02 » codders 2008-12-26 Added palette parsing 320 printPalette :: [PaletteEntry] -> IO()
321 printPalette [] = return ()
322 printPalette (x:xs) = do putStrLn $ show x
323 printPalette xs
324
e8984300 » codders 2008-12-27 Parsed out the tiles (proba... 325 -- Dumps the palette corresponding to an unpacked image
8f1112c8 » codders 2009-01-03 Parse map file separately f... 326 showPalette :: UnpackedFile -> IO ()
b2069a02 » codders 2008-12-26 Added palette parsing 327 showPalette image = do putStrLn $ "Data: " ++ (show $ L.length content)
328 case (paletteData content) of
329 Just palElements -> printPalette $ parsePalette palElements
330 Nothing -> putStrLn "Error getting palette"
8f1112c8 » codders 2009-01-03 Parse map file separately f... 331 where content = rawFileData image
4217b326 » codders 2009-01-03 Drawing... err... something 332
333 -- Reads the list of tiles from the image data
334 loadTileMap :: L.ByteString -> Maybe [W.Word8]
d5cc9c9c » codders 2009-01-04 Fixed up rendering by flipp... 335 loadTileMap imdata = do (head, rest) <- getBytes LEVELMAP_OFFSET_BYTES imdata
4217b326 » codders 2009-01-03 Drawing... err... something 336 (tilemap, rest) <- getBytes (IMAGE_WIDTH_TILES * IMAGE_HEIGHT_TILES) rest
337 return $ L.unpack tilemap
338