1local image = require 'image'
2local path = require 'path'
3
4local function newwriter()
5  return {
6    bytes = {},
7    write8 = function( self, x )
8      self.bytes[ #self.bytes + 1 ] = string.char( x & 255 )
9    end,
10    write16 = function( self, x )
11      self.bytes[ #self.bytes + 1 ] = string.char( ( x >> 8 ) & 255, x & 255 )
12    end,
13    write32 = function( self, x )
14      self.bytes[ #self.bytes + 1 ] = string.char( ( x >> 24 ) & 255, ( x >> 16 ) & 255, ( x >> 8 ) & 255, x & 255 )
15    end,
16    append = function( self, rle )
17      self.bytes[ #self.bytes + 1 ] = table.concat( rle.bytes )
18    end,
19    size = function( self )
20      self.bytes = { table.concat( self.bytes ) }
21      return #self.bytes[ 1 ]
22    end,
23    save = function( self, name )
24      local file, err = io.open( name, 'wb' )
25      if not file then error( err ) end
26      self.bytes = { table.concat( self.bytes ) }
27      file:write( self.bytes[ 1 ] )
28      file:close()
29    end
30  }
31end
32
33local function rgbto16( r, g, b )
34  r = r * 31 // 255
35  g = g * 63 // 255
36  b = b * 31 // 255
37
38  return ( r << 11 ) | ( g << 5 ) | b
39end
40
41local function getpixel( png, x, y )
42  local r, g, b = image.split( png:getPixel( x, y ) )
43  return rgbto16( r, g, b )
44end
45
46local function mktileset( images )
47  local writer = newwriter()
48
49  writer:write16( images[ 1 ]:getWidth() )
50  writer:write16( images[ 1 ]:getHeight() )
51  writer:write16( #images )
52
53  for _, img in pairs( images ) do
54    for y = 0, img:getHeight() - 1 do
55      for x = 0, img:getWidth() - 1 do
56        writer:write16( getpixel( img, x, y ) )
57      end
58    end
59  end
60
61  return writer
62end
63
64local function main( args )
65  if #args == 0 then
66    io.write[[
67rltileset.lua reads all images in the given directory and writes tileset data
68that can be directly fed to rl_tileset_create. The first image it finds in the
69given directory will dictate the width and height of all tiles in the tileset.
70Subsequent images with different dimensions won't be written to the tileset.
71
72
73Usage: luai rltileset.lua [ options ] <directory>
74
75--output <file>  writes the tileset to the given file
76                 (the default is <directory>.tls)
77
78]]
79
80    return 0
81  end
82
83  local dir, output
84
85  for i = 1, #args do
86    if args[ i ] == '--output' then
87      output = args[ i + 1 ]
88      i = i + 1
89    else
90      dir = args[ i ]
91    end
92  end
93
94  dir = path.realpath( dir )
95  local files = path.scandir( dir )
96  local width, height
97  local images = {}
98
99  for _, file in ipairs( files ) do
100    local stat = path.stat( file )
101
102    if stat.file then
103      local ok, png = pcall( image.load, file )
104
105      if ok then
106        if not width then
107          width, height = png:getSize()
108          images[ #images + 1 ] = png
109          io.write( string.format( '%s set the tileset dimensions to %dx%d\n', file, width, height ) )
110        elseif width == png:getWidth() and height == png:getHeight() then
111          images[ #images + 1 ] = png
112        else
113          io.write( string.format( '%s doesn\'t have the required dimensions\n', file ) )
114        end
115      else
116        io.write( string.format( '%s could not be read as an image\n', file ) )
117      end
118    end
119  end
120
121  if #images ~= 0 then
122    local writer = mktileset( images )
123
124    if not output then
125      local dir, name, ext = path.split( dir )
126      output = dir .. path.separator .. name .. '.tls'
127    end
128
129    writer:save( output )
130  else
131    io.write( 'no images were found\n' )
132  end
133
134  return 0
135end
136
137return main, mktileset
138