File system tutorial

Lua has some input and output facilities similar to the Standard I/O Library in C++. For portability reasons however, the standard io module in Lua does not offer access the underlying directory structure. This is where the LuaFileSystem library comes in. LFS is very light weight yet packs great functionality.

dodir

As a Lua programmer, you are probably familiar with the dofile function. dofile attempts to execute the contents of a given file as a chunk of Lua code. If you have a number of script files within a directory it becomes a bit of a chore to execute each one individually. Using LFS we can create a dodir function that scans and executes all the files within a directory. This is very handy, but be careful not to store any non-Lua files in the same directory or dodir will fail.

require ( 'lfs' )

function dodir ( path, recursive )
  for file in lfs.dir ( path ) do
    if file ~= '.' and file ~= '..' then
      local fullPath = path .. '/' .. file
      local attr = lfs.attributes ( fullPath )
      if attr.mode == 'directory' then
        if recursive == true then
          dodir ( fullPath, recursive )
        end
      else
        dofile ( fullPath )
      end
    end
  end
end

Image preloader

Next up, we are going to put LFS to use by making a simple image preloader script. We will write a coroutine function that iterates all the files within a given directory. The function loads each file as an image and stores a reference to it in a Lua table. After loading each image, the coroutine calculates how many bytes have been loaded and yields. Our preloader script resumes the coroutine from a timer and shows the loading progress on the screen.

require ( 'lfs' )

function LoadPathCo ( path, images )
  local fileSizes = {}
  local totalBytes = 0

  -- calculate the total number of bytes to be loaded
  for file in lfs.dir ( path ) do
    local fullPath = path .. '/' .. file
    local attr = lfs.attributes ( fullPath )
    if attr.mode ~= 'directory' then
      fileSizes[fullPath] = attr.size
      totalBytes = totalBytes + attr.size
    end
  end

  -- iterate the images yielding after loading each one
  local loadedBytes = 0
  for i, v in pairs ( fileSizes ) do
    images[i] = Image ( i )
    loadedBytes = loadedBytes + v

    -- return the percentage of bytes loaded
    coroutine.yield ( loadedBytes / totalBytes )
  end

  return 1
end

display:create ( "LFS", 640, 480, 32, true )

font = Font ( )
font:load_system ( "Arial", 10 )

sprite = Sprite ( )
sprite.canvas:set_font ( font )
display.viewport:add_child ( sprite )

-- create a new coroutine
co = coroutine.create ( LoadPathCo )

-- create a table that will store the loaded images
images = {}

timer = Timer ( )
timer.on_tick = function ( timer )
  -- supply a Lua table to be filled by the coroutine
  local result, loaded = coroutine.resume ( co, "Tutorials/tiles", images )

  if result == true then
    local sz = string.format ( "Loaded: %d%%", loaded * 100 )
    sprite.canvas:clear ( )
    sprite.canvas:write ( sz )
  else
    timer:stop ( )
  end
end
timer:start ( 16, true )

There are several limitations in the above script that you should note. First off, images can only be loaded synchronously so expect some unresponsiveness when dealing with very large files. Secondly, the preloader doesn't take into account the format of the files nor does it check if one of them fails to load. Ideally, you could expand the script to handle different sorts of assets such as sounds. The preloader in this example is quite simple, but it may come in handy as the number of assets in your project increases.

Download:  lfs.lua