Skip to content

Instantly share code, notes, and snippets.

@randomdude999
Last active May 22, 2016 18:35
Show Gist options
  • Save randomdude999/373bea17d86c76c97600a45d1f643f11 to your computer and use it in GitHub Desktop.
Save randomdude999/373bea17d86c76c97600a45d1f643f11 to your computer and use it in GitHub Desktop.
ComputerCraft archive format
-- a computercraft archive format
-- by randomdude999
local function get_rel_path(file, prefix)
local file = fs.combine(file,'') -- make path more correct
local prefix = fs.combine(prefix, '')
return fs.combine(file:gsub(prefix, ''), '')
end
local function recurseList(start)
local function yieldRecurseList(startPath, start)
local list = fs.list(startPath)
for _, file in ipairs(list) do
local path = fs.combine(startPath, file)
coroutine.yield(path, get_rel_path(path, start), fs.isDir(path))
if fs.isDir(path) then
yieldRecurseList(path, start)
end
end
end
return coroutine.wrap(function() yieldRecurseList(start or "/", start) end)
end
-- Binary file operations
-- Just in case the files contain some weird stuff
local function bin_read(path)
local f = fs.open(path, 'r')
local out = f.readAll()
f.close()
return out
end
local function bin_write(path, data)
local f = fs.open(path, 'w')
f.write(data)
f.close()
end
-- Escape functions
-- 0x00 is like an escape character
-- 0x00 0x01 is an escaped null (easier to parse than 0x00 0x00)
-- 0x00 0x02 is end of filename, beginning of file
-- 0x00 0x03 is end of file
-- 0x00 0x04 is beginning of file
-- 0x00 0x05 as contents means file is actually directory
local function escape(str)
return str:gsub("\0", "\0\1")
end
local function unescape(str)
return str:gsub("\0\1", "\0")
end
local function escape_file(file, str)
return "\0\4" .. escape(file) .. "\0\2" .. escape(str) .. "\0\3"
end
local function escape_dir(loc)
return "\0\4" .. escape(loc) .. "\0\2".."\0\5".."\0\3"
end
local function get_files(str)
local files = {}
local dirs = {}
for name, contents in str:gmatch("\0\4(.-)\0\2(.-)\0\3") do
if contents == "\0\5" then
dirs[#dirs+1] = name
else
files[name] = contents
end
end
for k, v in pairs(files) do
files[k] = unescape(v)
end
table.sort(dirs)
return files, dirs
end
function pack(dir)
if not fs.isDir(dir) then
error("Not a folder", 2)
end
local out = escape(fs.getName(dir))
for path, rel_path, isdir in recurseList(dir) do
if isdir then
out = out .. escape_dir(rel_path)
else
out = out .. escape_file(rel_path, bin_read(path))
end
end
return out
end
function unpack(str, to_where)
if not fs.isDir(to_where) then
error("Target must be directory", 2)
end
local name = str:match("^(.-)\0\4")
local location = fs.combine(to_where, name)
local files, dirs = get_files(str)
for _,dir in pairs(dirs) do
fs.makeDir(fs.combine(location, dir))
end
for fname,data in pairs(files) do
bin_write(fs.combine(location, fname), data)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment