APMT - Almost Persistent Mining Turtle
Description
+---------------------+------------+---------------------+
Installation
Copy one of these commands into your ComputerCraft terminal:
Pastebin:
pastebin get cUYTGbpb apmt_-_almost_persistent_mining_turtlewget:
wget https://pastebin.com/raw/cUYTGbpb apmt_-_almost_persistent_mining_turtleArchive:
wget https://cc.shobie.xyz/cc/get/pb-cUYTGbpb apmt_-_almost_persistent_mining_turtle
Quick Install:
wget https://cc.shobie.xyz/cc/get/pb-cUYTGbpb APMT - Almost Persistent Mining Turtle
Usage
Run the program after downloading
Tags
Source
View Original SourceCode Preview
-- +---------------------+------------+---------------------+
-- | | | |
-- | | BBPack | |
-- | | | |
-- +---------------------+------------+---------------------+
local version = "Version 1.6.1"
-- Pastebin uploader/downloader for ComputerCraft, by Jeffrey Alexander (aka Bomb Bloke).
-- Handles multiple files in a single paste, as well as non-ASCII symbols within files.
-- Used to be called "package".
-- http://www.computercraft.info/forums2/index.php?/topic/21801-
-- pastebin get cUYTGbpb bbpack
---------------------------------------------
------------Variable Declarations------------
---------------------------------------------
local band, brshift, blshift = bit.band, bit.brshift, bit.blshift
local b64 = {}
for i = 1, 64 do
b64[i - 1] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"):byte(i)
b64[("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"):sub(i, i)] = i - 1
end
---------------------------------------------
------------Function Declarations------------
---------------------------------------------
local unpack = unpack or table.unpack
local function snooze()
local myEvent = tostring({})
os.queueEvent(myEvent)
os.pullEvent(myEvent)
end
local function toBase64Internal(inputlist)
if type(inputlist) ~= "table" then error("bbpack.toBase64: Expected: table or file handle", 2) end
if inputlist.read then
local templist, len = {}, 1
for byte in inputlist.read do
templist[len] = byte
len = len + 1
end
inputlist.close()
inputlist = templist
elseif inputlist.readLine then
inputlist.close()
error("bbpack.toBase64: Use a binary-mode file handle", 2)
end
if #inputlist == 0 then return "" end
local curbit, curbyte, outputlist, len = 32, 0, {}, 1
for i = 1, #inputlist do
local inByte, mask = inputlist[i], 128
for j = 1, 8 do
if band(inByte, mask) == mask then curbyte = curbyte + curbit end
curbit, mask = curbit / 2, mask / 2
if curbit < 1 then
outputlist[len] = b64[curbyte]
curbit, curbyte, len = 32, 0, len + 1
end
end
end
if curbit > 1 then outputlist[len] = b64[curbyte] end
return string.char(unpack(outputlist))
end
local function fromBase64Internal(inData)
if type(inData) ~= "string" and type(inData) ~= "table" then error("bbpack.fromBase64: Expected: string or file handle", 2) end
if type(inData) == "table" then
if inData.readLine then
local temp = inData.readAll()
inData.close()
inData = temp
else
if inData.close then inData.close() end
error("bbpack.fromBase64: Use text-mode file handles", 2)
end
end
if #inData == 0 then return {} end
local curbyte, curbit, outputlist, len = 0, 128, {}, 1
for i = 1, #inData do
local mask, curchar = 32, b64[inData:sub(i, i)]
for j = 1, 6 do
if band(curchar, mask) == mask then curbyte = curbyte + curbit end
curbit, mask = curbit / 2, mask / 2
if curbit < 1 then
outputlist[len] = curbyte
curbit, curbyte, len = 128, 0, len + 1
end
end
end
if curbit > 1 and curbyte > 0 then outputlist[len] = curbyte end
return outputlist
end
local function compressIterator(ClearCode)
local startCodeSize = 1
while math.pow(2, startCodeSize) < ClearCode do startCodeSize = startCodeSize + 1 end
local EOI, ClearCode = math.pow(2, startCodeSize) + 1, math.pow(2, startCodeSize)
startCodeSize = startCodeSize + 1
local curstring, len, curbit, curbyte, outputlist, codes, CodeSize, MaxCode, nextcode, curcode = "", 2, 1, 0, {0}, {}, startCodeSize, math.pow(2, startCodeSize) - 1, EOI + 1
local function packByte(num)
local mask = 1
for i = 1, CodeSize do
if band(num, mask) == mask then curbyte = curbyte + curbit end
curbit, mask = curbit * 2, mask * 2
if curbit > 128 or (i == CodeSize and num == EOI) then
local counter = blshift(brshift(#outputlist - 1, 8), 8) + 1
outputlist[counter] = outputlist[counter] + 1
if outputlist[counter] > 255 then
outputlist[counter], outputlist[counter + 256], len = 255, 1, len + 1
snooze()
end
outputlist[len] = curbyte
curbit, curbyte, len = 1, 0, len + 1
end
end
end
packByte(ClearCode)
return function(incode)
if not incode then
if curcode then packByte(curcode) end
packByte(EOI)
outputlist[#outputlist + 1] = 0
return outputlist
end
if not curcode then
curcode = incode
return
end
curstring = curstring .. string.char(incode)
local thisCode = codes[curstring]
if thisCode then
curcode = thisCode
else
codes[curstring] = nextcode
nextcode = nextcode + 1
packByte(curcode)
if nextcode == MaxCode + 2 then
CodeSize = CodeSize + 1
MaxCode = math.pow(2, CodeSize) - 1
end
if nextcode == 4095 then
packByte(ClearCode)
CodeSize, MaxCode, nextcode, codes = startCodeSize, math.pow(2, startCodeSize) - 1, EOI + 1, {}
end
curcode, curstring = incode, string.char(incode)
end
end
end
local function compressInternal(inputlist, valRange)
if type(inputlist) ~= "table" and type(inputlist) ~= "string" then error("bbpack.compress: Expected: table, string or file handle", 2) end
if not valRange then valRange = 256 end
if type(valRange) ~= "number" or valRange < 2 or valRange > 256 then error("bbpack.compress: Value range must be a number between 2 - 256.", 2) end
if type(inputlist) == "table" and inputlist.close then
local templist
if inputlist.readAll then
templist = inputlist.readAll()
else
local len = 1
templist = {}
for thisByte in inputlist.read do
templist[len] = thisByte
len = len + 1
end
end
inputlist.close()
inputlist = templist
end
if type(inputlist) == "string" then inputlist = {inputlist:byte(1, #inputlist)} end
if #inputlist == 0 then return {} end
local compressIt = compressIterator(valRange)
local sleepCounter = 0
for i = 1, #inputlist do
compressIt(inputlist[i])
sleepCounter = sleepCounter + 1
if sleepCounter > 1023 then
sleepCounter = 0
snooze()
end
end
return compressIt(false)
end
local function decompressIterator(ClearCode, codelist)
local startCodeSize = 1
while math.pow(2, startCodeSize) < ClearCode do startCodeSize = startCodeSize + 1 end
local EOI, ClearCode = math.pow(2, startCodeSize) + 1, math.pow(2, startCodeSize)
startCodeSize = startCodeSize + 1
local lastcounter, curbyte, spot, CodeSize, MaxCode, maskbit, nextcode, codes, gotbytes = codelist[1], codelist[2], 3, startCodeSize, math.pow(2, startCodeSize) - 1, 1, EOI + 1, {}, 1
for i = 0, ClearCode - 1 do codes[i] = string.char(i) end
return function()
while true do
local curcode, curbit = 0, 1
for i = 1, CodeSize do
if band(curbyte, maskbit) == maskbit then curcode = curcode + curbit end
curbit, maskbit = curbit * 2, maskbit * 2
if maskbit > 128 and not (i == CodeSize and curcode == EOI) then
maskbit, curbyte, gotbytes = 1, codelist[spot], gotbytes + 1
spot = spot + 1
if gotbytes > lastcounter then
if curbyte == 0 then break end
lastcounter, gotbytes = curbyte, 1
curbyte = codelist[spot]
spot = spot + 1
snooze()
end
end
end
if curcode == ClearCode then
CodeSize, MaxCode, nextcode, codes = startCodeSize, math.pow(2, startCodeSize) - 1, EOI + 1, {}
for i = 0, ClearCode - 1 do codes[i] = string.char(i) end
elseif curcode ~= EOI then
if codes[nextcode - 1] then codes[nextcode - 1] = codes[nextcode - 1] .. codes[curcode]:sub(1, 1) else codes[nextcode - 1] = codes[curcode]:sub(1, 1) end
if nextcode < 4096 then
codes[nextcode] = codes[curcode]
nextcode = nextcode + 1
end
if nextcode - 2 == MaxCode then
CodeSize = CodeSize + 1
MaxCode = math.pow(2, CodeSize) - 1
end
return codes[curcode]
else return end
end
end
end
local function decompressInternal(codelist, outputText, valRange)
if type(codelist) ~= "table" then error("bbpack.decompress: Expected: table or file handle", 2) end
if not valRange then valRange = 256 end
if type(valRange) ~= "number" or valRange < 2 or valRange > 256 then error("bbpack.decompress: Value range must be a number between 2 - 256.", 2) end
if codelist.readLine then
codelist.close()
error("bbpack.decompress: Use binary-mode file handles", 2)
elseif codelist.readAll then
codelist = codelist.readAll()
codelist = {codelist:byte(1, #codelist)}
elseif codelist.read then
local data, len = {}, 1
while true do
local amount = codelist.read()
data[len] = amount
len = len + 1
if amount == 0 then break end
for i = 1, amount do
data[len] = codelist.read()
len = len + 1
end
snooze()
end
codelist = data
elseif #codelist == 0 then return outputText and "" or {} end
local outputlist, decompressIt, len = {}, decompressIterator(valRange, codelist), 1
local sleepCounter = 0
while true do
local output = decompressIt()
if output then
outputlist[len] = output
len = len + 1
else break end
end
outputlist = table.concat(outputlist)
return outputText and outputlist or {outputlist:byte(1, #outputlist)}
end
local function uploadPasteInternal(name, content)
if type(name) ~= "string" or (type(content) ~= "string" and type(content) ~= "table") then error("bbpack.uploadPaste: Expected: (string) paste name, (string or file handle) paste content", 2) end
if type(content) == "table" then
if content.readLine then
local temp = content.readAll()
content.close()
content = temp
else
if content.close then content.close() end
error("bbpack.uploadPaste: Use text-mode file handles", 2)
end
end
local webHandle = http.post(
"https://pastebin.com/api/api_post.php",
"api_option=paste&" ..
"api_dev_key=147764e5c6ac900a3015d77811334df1&" ..
"api_paste_format=lua&" ..
"api_paste_name=" .. textutils.urlEncode(name) .. "&" ..
"api_paste_code=" .. textutils.urlEncode(content))
if webHandle then
local response = webHandle.readAll()
webHandle.close()
return string.match(response, "[^/]+{{code}}quot;)
else error("Connection to pastebin failed. http API config in ComputerCraft.cfg is enabled, but may be set to block pastebin - or pastebin servers may be busy.") end
end
local function downloadPasteInternal(pasteID)
if type(pasteID) ~= "string" then error("bbpack.downloadPaste: Expected: (string) paste ID", 2) end
local webHandle = http.get("https://pastebin.com/raw/" .. textutils.urlEncode(pasteID))
if webHandle then
local incoming = webHandle.readAll()
webHandle.close()
return incoming
else error("Connection to pastebin failed. http API config in ComputerCraft.cfg is enabled, but may be set to block pastebin - or pastebin servers may be busy.") end
end
if shell then
---------------------------------------------
------------ Main Program ------------
---------------------------------------------
if not bbpack then os.loadAPI("bbpack") end
local args = {...}
if #args > 0 then args[1] = args[1]:lower() end
if #args < 1 or not (args[1] == "put" or args[1] == "get" or args[1] == "fileput" or args[1] == "fileget" or args[1] == "mount" or args[1] == "compress" or args[1] == "decompress" or args[1] == "cluster" or args[1] == "update") then
textutils.pagedPrint("Usage:\n")
textutils.pagedPrint("Uploads specified file or directory:")
textutils.pagedPrint("bbpack put [file/directory name]\n")
textutils.pagedPrint("Dumps paste into specified file or directory:")
textutils.pagedPrint("bbpack get <pasteID> [file/directory name]\n")
textutils.pagedPrint("Writes specified file or directory to archive file:")
textutils.pagedPrint("bbpack fileput [file/directory name] <target file>\n")
textutils.pagedPrint("Unpacks archive file to specified file or directory:")
textutils.pagedPrint("bbpack fileget <source file> [file/directory name]\n")
print("For the above options, if [file/directory name] is omitted the root of the drive will be used instead.\n")
textutils.pagedPrint("Enables automatic compression on hdd and compresses all existing files:")
textutils.pagedPrint("bbpack compress\n")
textutils.pagedPrint("Disables automatic compression on hdd and decompresses all existing files:")
textutils.pagedPrint("bbpack decompress\n")
textutils.pagedPrint("Mounts a given URL as the specified local file:")
textutils.pagedPrint("bbpack mount <URL> <fileName>\n")
textutils.pagedPrint("Turns the system into a server for the specified cluster:")
textutils.pagedPrint("bbpack cluster <clusterName>\n")
textutils.pagedPrint("Mounts the specified cluster as a drive:")
textutils.pagedPrint("bbpack mount <clusterName>\n")
textutils.pagedPrint("Updates package, reboots, and instructs all mounted cluster servers to do the same:")
textutils.pagedPrint("bbpack update\n")
return
end
if (args[1] == "put" or args[1] == "get") and not http then
print("BBPack's pastebin functionality requires that the http API be enabled.")
print("Shut down MineCraft game/server, set http_enable to true in ComputerCraft.cfg, then restart.")
error()
end
---------------------------------------------
------------ Uploading ------------
---------------------------------------------
if args[1] == "put" or args[1] == "fileput" then
local toFile
if args[1] == "fileput" then toFile = table.remove(args, #args) end
local uploadName, parent
if not args[2] then
print("Full system upload - are you sure? (y/n)")
if read():sub(1, 1):lower() ~= "y" then
print("Aborted.")
error()
end
uploadName = os.getComputerLabel()
if not uploadName then
print("Enter paste title:")
uploadName = read()
end
args[2] = ""
end
local target, output = shell.resolve(args[2]), {}
uploadName = uploadName or target
if not fs.exists(target) then
print("Invalid target.")
error()
end
if fs.isDir(target) then
local fileList = fs.list(target)
parent = target
while #fileList > 0 do
if fs.isDir(shell.resolve(fs.combine(parent, fileList[#fileList]))) then
local thisDir = table.remove(fileList, #fileList)
local newList = fs.list(shell.resolve(fs.combine(parent, thisDir)))
for i = 1, #newList do fileList[#fileList + 1] = fs.combine(thisDir, newList[i]) end
if #newList == 0 then output[#output + 1] = thisDir end
else output[#output + 1] = table.remove(fileList, #fileList) end
end
target = output
output = {}
else parent, target = "", {target} end
snooze()
for i = #target, 1, -1 do
if fs.combine(parent, target[i]) ~= shell.getRunningProgram() and not (parent == "" and fs.getDrive(target[i]) ~= "hdd") then
if fs.isDir(fs.combine(parent, target[i])) then
print(target[i])
output[#output + 1] = {target[i], true}
else
print(target[i])
output[#output + 1] = {target[i], toBase64Internal(compressInternal(fs.open(fs.combine(parent, target[i]), "rb")))}
snooze()
end
end
end
if toFile then
output = textutils.serialize(output)
if fs.getFreeSpace(shell.dir()) < #output then error("Output "..#output.." bytes, disk space available "..fs.getFreeSpace(shell.dir()).." bytes: file not written.") end
write("Writing to file \"" .. toFile .. "\"... ")
toFile = fs.open(shell.resolve(toFile), "w")
toFile.write(output)
toFile.close()
print("Success.")
else
write("Connecting to pastebin.com... ")
local response = uploadPasteInternal(uploadName, textutils.serialize(output))
print("Success.")
print("Uploaded to paste ID: " .. response)
print("Run \"bbpack get " .. response .. (parent == "" and "" or " " .. parent) .. "\" to download.")
end
---------------------------------------------
------------ Downloading ------------
---------------------------------------------
elseif args[1] == "get" or args[1] == "fileget" then
local incoming
if args[1] == "fileget" then
write("Attempting to read from archive... ")
if not fs.exists(shell.resolve(args[2])) then error("Can't find \"" .. shell.resolve(args[2]) .. "\".") end
local inFile = fs.open(shell.resolve(args[2]), "r")
incoming = textutils.unserialize(inFile.readAll())
inFile.close()
print("Success.")
else
write("Connecting to pastebin.com... ")
incoming = textutils.unserialize(downloadPasteInternal(args[2]))
print("Downloaded.")
end
local function getParent(path)
local pos = #path
if path:sub(pos,pos) == "/" then pos = pos - 1 end
while pos > 0 and path:sub(pos,pos) ~= "/" do pos = pos - 1 end
return pos > 0 and path:sub(1, pos - 1) or ""
end
local function writeFile(filePath, fileContent)
local path = fs.combine(shell.resolve("."), filePath)
if not fs.exists(getParent(path)) then fs.makeDir(getParent(path)) end
if fs.getFreeSpace(shell.dir()) < #fileContent then error(path.." "..#fileContent.." bytes, disk space available "..fs.getFreeSpace(shell.dir()).." bytes: file not written.") end
snooze()
local myFile = fs.open(path, "wb")
for i = 1, #fileContent do myFile.write(fileContent[i]) end
myFile.close()
snooze()
end
args[3] = args[3] or ""
if args[3] ~= "" and #incoming == 1 then
print(incoming[1][1] .. " => "..args[3])
writeFile(args[3], decompressInternal(fromBase64Internal(incoming[1][2])))
else
for i = 1, #incoming do
print(incoming[i][1])
if type(incoming[i][2]) == "string" then
writeFile(fs.combine(args[3], incoming[i][1]), decompressInternal(fromBase64Internal(incoming[i][2])))
else fs.makeDir(fs.combine(shell.resolve("."), fs.combine(args[3], incoming[i][1]))) end
incoming[i] = nil
end
end
---------------------------------------------
------------ Compress FS ------------
---------------------------------------------
elseif args[1] == "compress" then
bbpack.fileSys(true)
print("Filesystem compression enabled.")
---------------------------------------------
------------ Decompress FS ------------
---------------------------------------------
elseif args[1] == "decompress" then
print(bbpack.fileSys(false) and "Filesystem compression disabled." or "Filesystem compression disabled, but space is insufficient to decompress all files.")
---------------------------------------------
------------ Mount ------------
---------------------------------------------
elseif args[1] == "mount" then
bbpack.fileSys(args[2], args[3] and shell.resolve(args[3]))
print("Successfully mounted.")
---------------------------------------------
------------ Cluster ------------
---------------------------------------------
elseif args[1] == "cluster" then
local cluster, protocol = args[2], rednet.host and args[2]
rfs.makeDir(cluster)
for _, side in pairs(rs.getSides()) do if peripheral.getType(side) == "modem" then rednet.open(side) end end
print("Running as part of cluster \"" .. cluster .. "\"...")
local function locFile(path)
local matches = rfs.find(path .. "*")
for i = 1, #matches do
local thisMatch = matches[i]
if #thisMatch == #path + 3 and thisMatch:sub(1, #path) == path then return thisMatch end
end
return nil
end
return (function() while true do
local sender, msg = rednet.receive(protocol)
if type(msg) == "table" and msg.cluster == cluster then
local command, par1, par2 = unpack(msg)
if command == "rollcall" then
rednet.send(sender, {["cluster"] = cluster, ["uuid"] = msg.uuid, "rollcallResponse"}, protocol)
elseif command == "isDir" then
rednet.send(sender, {["cluster"] = cluster, ["uuid"] = msg.uuid, rfs.isDir(par1)}, protocol)
elseif command == "makeDir" then
rfs.makeDir(par1)
elseif command == "exists" then
rednet.send(sender, {["cluster"] = cluster, ["uuid"] = msg.uuid, type(locFile(par1)) == "string"}, protocol)
elseif command == "getFreeSpace" then
rednet.send(sender, {["cluster"] = cluster, ["uuid"] = msg.uuid, {rfs.getFreeSpace("") - 10000, os.getComputerID()}}, protocol)
elseif command == "getSize" then
local path = locFile(par1)
rednet.send(sender, {["cluster"] = cluster, ["uuid"] = msg.uuid, path and rfs.getSize(path) or 0}, protocol)
elseif command == "delete" then
local path = locFile(par1)
if path then rfs.delete(path) end
elseif command == "list" then
local list = rfs.list(par1)
for i = 1, #list do
local entry = list[i]
if not fs.isDir(fs.combine(par1, entry)) then list[i] = entry:sub(1, -4) end
end
rednet.send(sender, {["cluster"] = cluster, ["uuid"] = msg.uuid, list}, protocol)
elseif command == "get" then
local path = locFile(par1)
if path then
local file, content = rfs.open(path, "rb")
if file.readAll then
content = file.readAll()
else
content = {}
local counter = 1
for byte in file.read do
content[counter] = byte
counter = counter + 1
end
content = string.char(unpack(content))
end
file.close()
rednet.send(sender, {["cluster"] = cluster, ["uuid"] = msg.uuid, {tonumber(path:sub(-3)), content}}, protocol)
end
rednet.send(sender, {["cluster"] = cluster, ["uuid"] = msg.uuid, false}, protocol)
elseif command == "put" then
local file = rfs.open(par1, "wb")
if term.setPaletteColour then
file.write(par2)
else
par2 = {par2:byte(1, #par2)}
for i = 1, #par2 do file.write(par2[i]) end
end
file.close()
elseif command == "update" then
local file = rfs.open("bbpack", "w")
file.write(downloadPasteInternal("cUYTGbpb"))
file.close()
os.reboot()
end
end
end end)()
---------------------------------------------
------------ Update ------------
---------------------------------------------
elseif args[1] == "update" then
bbpack.update()
end
else
---------------------------------------------
------------ Load As API ------------
---------------------------------------------
compress = compressInternal
decompress = decompressInternal
toBase64 = toBase64Internal
fromBase64 = fromBase64Internal
uploadPaste = uploadPasteInternal
downloadPaste = downloadPasteInternal
function open(file, mode, valRange)
if (type(file) ~= "table" and type(file) ~= "string") or type(mode) ~= "string" then error("bbpack.open: Expected: file (string or handle), mode (string). Got: " .. type(file) .. ", " .. type(mode) .. ".", 2) end
mode = mode:lower()
local binary, append, read, write, newhandle = mode:find("b") ~= nil, mode:find("a") ~= nil, mode:find("r") ~= nil, mode:find("w") ~= nil, {}
if not valRange then valRange = 256 end
if type(valRange) ~= "number" or valRange < 2 or valRange > 256 then error("bbpack.decompress: Value range must be a number between 2 - 256.", 2) end
if not (append or write or read) then error("bbpack.open: Invalid file mode: " .. mode, 2) end
if type(file) == "string" then
if append and rfs.exists(file) then
local oldfile = open(file, binary and "rb" or "r", valRange)
if not oldfile then return nil end
local olddata = oldfile.readAll()
oldfile.close()
newhandle = open(file, binary and "wb" or "w", valRange)
newhandle.write(olddata)
return newhandle
end
file = rfs.open(file, (read and "r" or "w") .. "b")
if not file then return nil end
else
if (write and (file.writeLine or not file.write)) or (read and not file.read) then error("bbpack.open: Handle / mode mismatch.", 2) end
local tempfile, keys = {}, {}
for key, _ in pairs(file) do keys[#keys + 1] = key end
for i = 1, #keys do
tempfile[keys[i]] = file[keys[i]]
file[keys[i]] = nil
end
file = tempfile
end
if read then
local data = {}
if file.readAll then
local len = 1
while true do
local amount = file.read()
data[len] = string.char(amount)
len = len + 1
if amount == 0 then break end
data[len] = file.read(amount)
len = len + 1
end
data = table.concat(data)
data = {data:byte(1, #data)}
else
local len = 1
while true do
local amount = file.read()
data[len] = amount
len = len + 1
if amount == 0 then break end
for i = 1, amount do
data[len] = file.read()
len = len + 1
end
snooze()
end
end
local decompressIt, outputlist = decompressIterator(valRange, data), ""
if binary then
function newhandle.read(amount)
if not outputlist then return nil end
if type(amount) ~= "number" then
if #outputlist == 0 then
outputlist = decompressIt()
if not outputlist then return nil end
end
local result = outputlist:byte(1)
outputlist = outputlist:sub(2)
return result
else
while #outputlist < amount do
local new = decompressIt()
if not new then
new = outputlist
outputlist = nil
if #new > 0 then return new else return end
end
outputlist = outputlist .. new
end
local result = outputlist:sub(1, amount)
outputlist = outputlist:sub(amount + 1)
return result
end
end
function newhandle.readAll()
if not outputlist then return nil end
local result, len = {outputlist}, 2
for data in decompressIt do
result[len] = data
len = len + 1
end
outputlist = nil
return table.concat(result)
end
else
function newhandle.readLine()
if not outputlist then return nil end
while not outputlist:find("\n") do
local new = decompressIt()
if not new then
new = outputlist
outputlist = nil
if #new > 0 then return new else return end
end
outputlist = outputlist .. new
end
local result = outputlist:sub(1, outputlist:find("\n") - 1)
outputlist = outputlist:sub(outputlist:find("\n") + 1)
if outputlist:byte(1) == 13 then outputlist = outputlist:sub(2) end
return result
end
function newhandle.readAll()
if not outputlist then return nil end
local result, len = {outputlist}, 2
for data in decompressIt do
result[len] = data
len = len + 1
end
outputlist = nil
return table.concat(result)
end
end
function newhandle.extractHandle()
local keys = {}
for key, _ in pairs(newhandle) do keys[#keys + 1] = key end
for i = 1, #keys do newhandle[keys[i]] = nil end
return file
end
else
local compressIt = compressIterator(valRange)
if binary then
function newhandle.write(data)
if type(data) == "number" then
compressIt(data)
elseif type(data) == "string" then
data = {data:byte(1, #data)}
for i = 1, #data do compressIt(data[i]) end
else error("bbpackHandle.write: bad argument #1 (string or number expected, got " .. type(data) .. ")", 2) end
end
else
function newhandle.write(text)
text = tostring(text)
text = {text:byte(1, #text)}
for i = 1, #text do compressIt(text[i]) end
end
function newhandle.writeLine(text)
text = tostring(text)
text = {text:byte(1, #text)}
for i = 1, #text do compressIt(text[i]) end
compressIt(10)
end
end
newhandle.flush = file.flush
function newhandle.extractHandle()
local output, fWrite = compressIt(false), file.write
for j = 1, #output do fWrite(output[j]) end
local keys = {}
for key, _ in pairs(newhandle) do keys[#keys + 1] = key end
for i = 1, #keys do newhandle[keys[i]] = nil end
return file
end
end
function newhandle.close()
newhandle.extractHandle().close()
end
return newhandle
end
function lines(file)
if type(file) == "string" then
file = open(file, "r")
elseif type(file) ~= "table" or not file.readLine then
error("bbpack.lines: Expected: file (string or \"r\"-mode handle).", 2)
end
return function()
if not file.readLine then return nil end
local line = file.readLine()
if line then
return line
else
file.close()
return nil
end
end
end
local function dividePath(path)
local result = {}
for element in path:gmatch("[^/]+") do result[#result + 1] = element end
return result
end
local function getGithubRepo(repo)
local elements = dividePath(repo)
for i = 1, #elements do if table.remove(elements, 1) == "github.com" then break end end
if #elements < 2 or elements[3] == "raw" then return end
repo = elements[1] .. "/" .. elements[2]
local branch = (elements[3] == "tree") and elements[4] or "master"
local webHandle = http.get("https://api.github.com/repos/" .. repo .. "/git/trees/" .. branch .. "?recursive=1")
if not webHandle then return end
local json = textutils.unserialize(webHandle.readAll():gsub("\10", ""):gsub(" ", ""):gsub("%[", "{"):gsub("]", "}"):gsub("{\"", "{[\""):gsub(",\"", ",[\""):gsub("\":", "\"]="))
webHandle.close()
if json.message == "Not Found" then return end
local tree, results = json.tree, {}
for i = 1, #tree do if tree[i].type == "blob" then
local path, cur = tree[i].path, results
local elements = dividePath(path)
for i = 1, #elements - 1 do
local element = elements[i]
if not cur[element] then cur[element] = {} end
cur = cur[element]
end
cur[elements[#elements]] = "https://raw.githubusercontent.com/" .. repo .. "/" .. branch .. "/" .. path
end end
if #elements > 4 then for i = 5, #elements do results = results[elements[i]] end end
return (type(results) == "table") and results
end
local configTable = {["webMounts"] = {}, ["githubRepos"] = {}, ["clusters"] = {}, ["compressedFS"] = false}
if fs.exists(".bbpack.cfg") then
local file = rfs and rfs.open(".bbpack.cfg", "r") or fs.open(".bbpack.cfg", "r")
local input = textutils.unserialize(file.readAll())
file.close()
if type(input) == "table" then
if type(input.webMounts) == "table" then configTable.webMounts = input.webMounts end
if type(input.githubRepos) == "table" then configTable.githubRepos = input.githubRepos end
if type(input.clusters) == "table" then configTable.clusters = input.clusters end
if type(input.compressedFS) == "boolean" then configTable.compressedFS = input.compressedFS end
end
end
local webMountList, clusterList, repoList = configTable.webMounts, configTable.clusters, {}
for path, url in pairs(configTable.githubRepos) do repoList[path] = getGithubRepo(url) end
if next(clusterList) then for _, side in pairs(rs.getSides()) do if peripheral.getType(side) == "modem" then rednet.open(side) end end end
local blacklist = {"bbpack", "bbpack.lua", "startup", "startup.lua", ".settings", ".gif", ".zip", ".bbpack.cfg"}
if not _G.rfs then
local rfs, ramdisk = {}, {}
for key, value in pairs(fs) do rfs[key] = value end
local function clusterTalk(cluster, answer, ...)
local target, uuid, result, sender, msg = clusterList[cluster], math.random(1, 0x7FFFFFFF), {}
for i = 1, #target do rednet.send(target[i], {["cluster"] = cluster, ["uuid"] = uuid, unpack(arg)}, rednet.host and cluster) end
if answer then
for i = 1, #target do
repeat sender, msg = rednet.receive(rednet.host and cluster) until type(msg) == "table" and msg.cluster == cluster and msg.uuid == uuid
result[i] = msg[1]
end
return result
end
end
_G.fs.list = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
path = fs.combine(path, "")
local elements = dividePath(path)
if not fs.isDir(path) then error("Not a directory", 2) end
if fs.getDrive(path) == "hdd" then
local results = rfs.list(path)
for i = 1, #results do
local thisResult = results[i]
if thisResult:sub(-4) == ".bbp" then results[i] = thisResult:sub(1, -5) end
end
for mount in pairs(webMountList) do
local mountElements = dividePath(mount)
if #elements == #mountElements - 1 then
local match = true
for i = 1, #elements do if elements[i] ~= mountElements[i] then
match = false
break
end end
if match then results[#results + 1] = mountElements[#mountElements] end
end
end
if path == "" then
results[#results + 1] = "ram"
for cluster in pairs(clusterList) do results[#results + 1] = cluster end
for repo in pairs(repoList) do results[#results + 1] = repo end
end
table.sort(results)
return results
elseif clusterList[elements[1]] then
local results = {}
local lists = clusterTalk(elements[1], true, "list", path)
for i = 1, #clusterList[elements[1]] do
local subList = lists[i]
for i = 1, #subList do
local found, thisSub = false, subList[i]
for j = 1, #results do if results[j] == thisSub then
found = true
break
end end
if not found then results[#results + 1] = thisSub end
end
end
table.sort(results)
return results
elseif elements[1] == "ram" or repoList[elements[1]] then
local cur, results = (elements[1] == "ram") and ramdisk or repoList[elements[1]], {}
for i = 2, #elements do cur = cur[elements[i]] end
for entry in pairs(cur) do results[#results + 1] = entry end
table.sort(results)
return results
else
return rfs.list(path)
end
end
_G.fs.exists = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
path = fs.combine(path, "")
local elements = dividePath(path)
if webMountList[path] then
return true
elseif clusterList[elements[1]] then
if #elements == 1 then return true end
local list = clusterTalk(elements[1], true, "exists", path)
for i = 1, #list do if list[i] then return true end end
return false
elseif elements[1] == "ram" or repoList[elements[1]] then
local cur = (elements[1] == "ram") and ramdisk or repoList[elements[1]]
for i = 2, #elements do
cur = cur[elements[i]]
if not cur then return false end
end
return true
else
return rfs.exists(path..".bbp") or rfs.exists(path)
end
end
_G.fs.isDir = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
if not fs.exists(path) then return false end
path = fs.combine(path, "")
local elements = dividePath(path)
if clusterList[elements[1]] then
if #elements == 1 then return true end
local list = clusterTalk(elements[1], true, "isDir", path)
return list[1]
elseif elements[1] == "ram" or repoList[elements[1]] then
local cur = (elements[1] == "ram") and ramdisk or repoList[elements[1]]
for i = 2, #elements do
cur = cur[elements[i]]
end
return type(cur) == "table"
else
return rfs.isDir(path)
end
end
_G.fs.isReadOnly = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
path = fs.combine(path, "")
local elements = dividePath(path)
if webMountList[path] or repoList[elements[1]] then
return true
elseif clusterList[elements[1]] or elements[1] == "ram" then
return false
else
return rfs.isReadOnly(rfs.exists(path..".bbp") and (path..".bbp") or path)
end
end
_G.fs.getDrive = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
path = fs.combine(path, "")
local elements = dividePath(path)
if clusterList[elements[1]] or elements[1] == "ram" or repoList[elements[1]] then
return fs.exists(path) and elements[1]
else
return rfs.getDrive(rfs.exists(path..".bbp") and (path..".bbp") or path)
end
end
_G.fs.getSize = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
path = fs.combine(path, "")
local elements = dividePath(path)
if webMountList[path] or repoList[elements[1]] then
return 0
elseif clusterList[elements[1]] then
if #elements == 1 then return 0 end
if not fs.exists(path) then error("No such file", 2) end
local size, list = 0, clusterTalk(elements[1], true, "getSize", path)
for i = 1, #clusterList[elements[1]] do size = size + list[i] end
return size
elseif elements[1] == "ram" then
local cur = ramdisk
for i = 2, #elements do
cur = cur[elements[i]]
if not cur then error("No such file", 2) end
end
return type(cur) == "string" and #cur or 0
else
return rfs.getSize(rfs.exists(path..".bbp") and (path..".bbp") or path)
end
end
_G.fs.getFreeSpace = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
path = fs.combine(path, "")
local elements = dividePath(path)
if clusterList[elements[1]] then
local size, list = 0, clusterTalk(elements[1], true, "getFreeSpace")
for i = 1, #clusterList[elements[1]] do size = size + list[i][1] end
return size
elseif elements[1] == "ram" then
return math.huge
elseif repoList[elements[1]] then
return 0
else
return rfs.getFreeSpace(path)
end
end
_G.fs.makeDir = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
path = fs.combine(path, "")
local elements = dividePath(path)
if fs.exists(path) then
if fs.isDir(path) then
return
else
error("File exists", 2)
end
end
if clusterList[elements[1]] then
clusterTalk(elements[1], false, "makeDir", path)
elseif elements[1] == "ram" then
local cur = ramdisk
for i = 2, #elements do
local next = cur[elements[i]]
if next then
cur = next
else
cur[elements[i]] = {}
cur = cur[elements[i]]
end
end
elseif repoList[elements[1]] then
error("Access denied", 2)
else
return rfs.makeDir(path)
end
end
_G.fs.move = function(path1, path2)
if type(path1) ~= "string" then error("bad argument #1 (expected string, got " .. type(path1) .. ")", 2 ) end
if type(path2) ~= "string" then error("bad argument #2 (expected string, got " .. type(path2) .. ")", 2 ) end
path1, path2 = fs.combine(path1, ""), fs.combine(path2, "")
if not fs.exists(path1) then error("No such file", 2) end
if fs.exists(path2) then error("File exists", 2) end
if fs.isReadOnly(path1) or fs.isReadOnly(path2) or (#dividePath(path1) == 1 and fs.getDrive(path1) ~= "hdd") then error("Access denied", 2) end
if #dividePath(path1) < #dividePath(path2) and path2:sub(#path1) == path1 then error("Can't copy a directory inside itself", 2) end
-- ... and if we run out of space we'll just let things fall over at the writing level.
if fs.isDir(path1) then
fs.makeDir(path2)
local list = fs.list(path1)
for i = 1, #list do fs.move(fs.combine(path1, list[i]), fs.combine(path2, list[i])) end
else
local input, output = fs.open(path1, "rb"), fs.open(path2, "wb")
if input.readAll then
output.write(input.readAll())
else
for byte in input.read do output.write(byte) end
end
input.close()
output.close()
end
fs.delete(path1)
end
_G.fs.copy = function(path1, path2)
if type(path1) ~= "string" then error("bad argument #1 (expected string, got " .. type(path1) .. ")", 2 ) end
if type(path2) ~= "string" then error("bad argument #2 (expected string, got " .. type(path2) .. ")", 2 ) end
path1, path2 = fs.combine(path1, ""), fs.combine(path2, "")
if not fs.exists(path1) then error("No such file", 2) end
if fs.exists(path2) then error("File exists", 2) end
if fs.isReadOnly(path2) then error("Access denied", 2) end
if #dividePath(path1) < #dividePath(path2) and path2:sub(#path1) == path1 then error("Can't copy a directory inside itself", 2) end
-- ... and if we run out of space we'll just let things fall over at the writing level.
if fs.isDir(path1) then
fs.makeDir(path2)
local list = fs.list(path1)
for i = 1, #list do fs.copy(fs.combine(path1, list[i]), fs.combine(path2, list[i])) end
else
local input, output = fs.open(path1, "rb"), fs.open(path2, "wb")
if input.readAll then
output.write(input.readAll())
else
for byte in input.read do output.write(byte) end
end
input.close()
output.close()
end
end
_G.fs.delete = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
path = fs.combine(path, "")
local elements = dividePath(path)
if not fs.exists(path) then return end
if webMountList[path] then
webMountList[path] = nil
local file = rfs.open(".bbpack.cfg", "w")
file.write(textutils.serialize(configTable))
file.close()
elseif clusterList[elements[1]] then
if #elements == 1 then
clusterList[elements[1]] = nil
local file = rfs.open(".bbpack.cfg", "w")
file.write(textutils.serialize(configTable))
file.close()
else
clusterTalk(elements[1], false, "delete", path)
end
elseif repoList[elements[1]] then
if #elements == 1 then
repoList[elements[1]], configTable.githubRepos[elements[1]] = nil, nil
local file = rfs.open(".bbpack.cfg", "w")
file.write(textutils.serialize(configTable))
file.close()
else
error("Access denied", 2)
end
elseif elements[1] == "ram" then
if #elements == 1 then error("Access denied", 2) end
local cur = ramdisk
for i = 2, #elements - 1 do
cur = cur[elements[i]]
if not cur then return end
end
cur[elements[#elements]] = nil
else
if fs.isDir(path) then
local list = fs.list(path)
for i = 1, #list do
local ok, err = pcall(fs.delete, fs.combine(path, list[i]))
if not ok then error(err:gsub("pcall: ", ""), 2) end
end
return rfs.delete(path)
else
return rfs.delete(rfs.exists(path..".bbp") and (path..".bbp") or path)
end
end
end
_G.fs.open = function(path, mode)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
if type(mode) ~= "string" then error("bad argument #2 (expected string, got " .. type(mode) .. ")", 2 ) end
path = fs.combine(path, "")
local elements = dividePath(path)
mode = mode:lower()
local binary, append, read, write = mode:find("b") ~= nil, mode:find("a") ~= nil, mode:find("r") ~= nil, mode:find("w") ~= nil
if webMountList[path] or clusterList[elements[1]] or elements[1] == "ram" or repoList[elements[1]] then
if not (append or write or read) then error("Invalid file mode: " .. mode, 2) end
if read then
if not fs.exists(path) or fs.isDir(path) then return nil, "No such file" end
local data
local handle = {["close"] = function() data = nil end}
if webMountList[path] then
local webHandle = http.get(webMountList[path], nil, term.setPaletteColour and true)
data = webHandle.readAll()
webHandle.close()
elseif clusterList[elements[1]] then
data = {}
local list = clusterTalk(elements[1], true, "get", path)
for i = 1, #clusterList[elements[1]] do if list[i] then data[list[i][1]] = list[i][2] end end
data = table.concat(data)
data = decompressInternal({data:byte(1, #data)}, true)
elseif repoList[elements[1]] then
data = repoList[elements[1]]
for i = 2, #elements do data = data[elements[i]] end
local webHandle = http.get(data, nil, term.setPaletteColour and true)
data = webHandle.readAll()
webHandle.close()
else
data = ramdisk
for i = 2, #elements do data = data[elements[i]] end
end
if #data == 0 then data = nil end
if binary then
handle.read = function(amount)
if not data then return nil end
local result
if type(amount) ~= "number" then
result = data:byte(1)
data = data:sub(2)
else
result = data:sub(1, amount)
data = data:sub(amount + 1)
end
if #data == 0 then data = nil end
return result
end
handle.readAll = function()
if not data then return nil end
local result = data
data = nil
return result
end
else
handle.readLine = function()
if not data then return nil end
if data:find("\n") then
local result = data:sub(1, data:find("\n") - 1)
data = data:sub(data:find("\n") + 1)
if data:byte(1) == 13 then data = data:sub(2) end
return result
else
local result = data
data = nil
return data
end
end
handle.readAll = function()
if not data then return nil end
local result = data
data = nil
return result
end
end
return handle
elseif write or append then
if webMountList[path] or repoList[elements[1]] then return nil, "Access denied" end
if fs.isDir(path) then return nil, "Cannot write to directory" end
fs.makeDir(fs.getDir(path))
local handle, output, counter = {}, {}, 1
if binary then
handle.write = function(data)
if type(data) ~= "string" and type(data) ~= "number" then error("bad argument #1 (string or number expected, got " .. type(data) .. ")", 2) end
output[counter] = type(data) == "number" and string.char(data) or data
counter = counter + 1
end
else
handle.write = function(data)
output[counter] = tostring(data)
counter = counter + 1
end
handle.writeLine = function(data)
output[counter] = tostring(data)
output[counter + 1] = "\n"
counter = counter + 2
end
end
local ramLink, ramIndex
if clusterList[elements[1]] and append and fs.exists(path) then
local data, list = {}, clusterTalk(elements[1], true, "get", path)
for i = 1, #list do if list[i] then data[list[i][1]] = list[i][2] end end
data = table.concat(data)
output[1], counter = decompressInternal({data:byte(1, #data)}, true), 2
elseif elements[1] == "ram" then
ramLink = ramdisk
for i = 2, #elements - 1 do ramLink = ramLink[elements[i]] end
ramIndex = elements[#elements]
if (append and not ramLink[ramIndex]) or not append then ramLink[ramIndex] = "" end
end
handle.flush = function()
if clusterList[elements[1]] then
output, counter = {table.concat(output)}, 1
local data, segs, pos, totalSpace, thisCluster = string.char(unpack(compressInternal(output[1]))), 1, 1, fs.getFreeSpace(elements[1]), clusterList[elements[1]]
if fs.exists(path) then totalSpace = totalSpace + fs.getSize(path) end
if totalSpace < #data then error("Out of space", 2) end
fs.delete(path)
local spaceList = clusterTalk(elements[1], true, "getFreeSpace")
for i = 1, #thisCluster do
local thisSpace = spaceList[i][1]
if thisSpace > 0 then
local segString = tostring(segs)
rednet.send(spaceList[i][2], {["cluster"] = elements[1], "put", path .. string.rep("0", 3 - #segString) .. segString, data:sub(pos, pos + thisSpace - 1)}, rednet.host and elements[1])
pos, segs = pos + thisSpace, segs + 1
end
if pos > #data then break end
end
else
output = table.concat(output)
if append then output = ramLink[ramIndex] .. output end
ramLink[ramIndex] = output
output, counter, append = {}, 1, true
end
end
handle.close = function()
handle.flush()
for key in pairs(handle) do handle[key] = function() end end
end
return handle
end
else
if (write or append) and rfs.isReadOnly(path) then return nil, "Access denied" end
for i = 1, #blacklist do
local check = blacklist[i]
if path:sub(-#check):lower() == check then return rfs.open(path, mode) end
end
if read then
return rfs.exists(path .. ".bbp") and open(path .. ".bbp", mode) or rfs.open(path, mode)
elseif configTable.compressedFS then
if rfs.getDrive(elements[1]) and rfs.getDrive(elements[1]) ~= "hdd" then
return rfs.open(path, mode)
elseif append then
if rfs.exists(path) then
local file, content = rfs.open(path, binary and "rb" or "r")
if file.readAll then
content = file.readAll()
else
content = {}
for byte in file.read do content[#content + 1] = byte end
content = string.char(unpack(content))
end
file.close()
rfs.delete(path)
file = open(path .. ".bbp", binary and "wb" or "w")
file.write(content)
return file
else return open(path .. ".bbp", mode) end
elseif write then
if rfs.exists(path) then rfs.delete(path) end
return open(path .. ".bbp", mode)
end
else
if append then
if rfs.exists(path .. ".bbp") then
local file = open(path .. ".bbp", binary and "rb" or "r")
local content = file.readAll()
file.close()
rfs.delete(path .. ".bbp")
file = rfs.open(path, binary and "wb" or "w")
if file.writeLine or term.setPaletteColour then
file.write(content)
else
content = {string.byte(1, #content)}
for i = 1, #content do file.write(content[i]) end
end
return file
else return rfs.open(path, mode) end
elseif write then
if rfs.exists(path .. ".bbp") then rfs.delete(path .. ".bbp") end
return rfs.open(path, mode)
end
end
error("Unsupported mode", 2)
end
end
_G.fs.find = function(path)
if type(path) ~= "string" then error("bad argument #1 (expected string, got " .. type(path) .. ")", 2 ) end
local pathParts, results, curfolder = {}, {}, "/"
for part in path:gmatch("[^/]+") do pathParts[#pathParts + 1] = part:gsub("*", "[^/]*") end
if #pathParts == 0 then return {} end
local prospects = fs.list(curfolder)
for i = 1, #prospects do prospects[i] = {["parent"] = curfolder, ["depth"] = 1, ["name"] = prospects[i]} end
while #prospects > 0 do
local thisProspect = table.remove(prospects, 1)
local fullPath = fs.combine(thisProspect.parent, thisProspect.name)
if thisProspect.name == thisProspect.name:match(pathParts[thisProspect.depth]) then
if thisProspect.depth == #pathParts then
results[#results + 1] = fullPath
elseif fs.isDir(fullPath) and thisProspect.depth < #pathParts then
local newList = fs.list(fullPath)
for i = 1, #newList do prospects[#prospects + 1] = {["parent"] = fullPath, ["depth"] = thisProspect.depth + 1, ["name"] = newList[i]} end
end
end
end
return results
end
_G.rfs = rfs
end
update = bbpack and bbpack.update or function()
for cluster, ids in pairs(clusterList) do for i = 1, #ids do
rednet.send(ids[i], {["cluster"] = cluster, "update"}, rednet.host and cluster)
end end
local file = rfs.open("bbpack", "w")
file.write(downloadPasteInternal("cUYTGbpb"))
file.close()
os.reboot()
end
fileSys = bbpack and bbpack.fileSys or function(par1, par2)
if type(par1) == "boolean" or (type(par1) == "string" and type(par2) == "boolean") then
-- Compress / decompress hdd contents.
local list
if type(par1) == "boolean" then
list = rfs.list("")
configTable.compressedFS = par1
else
list = {par1}
par1 = par2
end
while #list > 0 do
local entry = list[#list]
list[#list] = nil
if rfs.getDrive(entry) == "hdd" and not webMountList[entry] then
if rfs.isDir(entry) then
local newList, curLen = rfs.list(entry), #list
for i = 1, #newList do list[curLen + i] = fs.combine(entry, newList[i]) end
else
local blacklisted = false
for i = 1, #blacklist do
local check = blacklist[i]
if entry:sub(-#check):lower() == check then
blacklisted = true
break
end
end
if not blacklisted then
if par1 and entry:sub(-4) ~= ".bbp" then
-- Compress this file.
local file, content = rfs.open(entry, "rb")
if file.readAll then
content = file.readAll()
else
content = {}
local counter = 1
for byte in file.read do
content[counter] = byte
counter = counter + 1
end
content = string.char(unpack(content))
end
file.close()
content = compressInternal(content)
if rfs.getFreeSpace(entry) + rfs.getSize(entry) < #content then return false end
rfs.delete(entry)
snooze()
file = rfs.open(entry .. ".bbp", "wb")
if term.setPaletteColor then
file.write(string.char(unpack(content)))
else
for i = 1, #content do file.write(content[i]) end
end
file.close()
snooze()
elseif not par1 and entry:sub(-4) == ".bbp" then
-- Decompress this file.
local file = open(entry, "rb")
local content = file.readAll()
file.close()
if rfs.getFreeSpace(entry) + rfs.getSize(entry) < #content then return false end
rfs.delete(entry)
snooze()
file = rfs.open(entry:sub(1, -5), "wb")
if term.setPaletteColor then
file.write(content)
else
content = {content:byte(1, #content)}
for i = 1, #content do file.write(content[i]) end
end
file.close()
snooze()
end
end
end
end
end
elseif type(par1) == "string" and type(par2) == "string" then
-- New web mount.
local url, path = par1, fs.combine(par2, "")
local elements = dividePath(path)
local repo = getGithubRepo(url)
if repo then
if #elements > 1 then error("bbpack.mount: Github repos must be mounted at the root of your file system", 2) end
repoList[path] = repo
configTable.githubRepos[path] = url
else
if fs.getDrive(elements[1]) and fs.getDrive(elements[1]) ~= "hdd" then error("bbpack.mount: web mounts must be located on the main hdd", 2) end
local get = http.get(url)
if not get then error("bbpack.mount: Can't connect to URL: "..url, 2) end
get.close()
webMountList[path] = url
end
elseif type(par1) == "string" then
-- New cluster mount.
local cluster, uuid = par1, math.random(1, 0x7FFFFFFF)
for _, side in pairs(rs.getSides()) do if peripheral.getType(side) == "modem" then rednet.open(side) end end
rednet.broadcast({["cluster"] = cluster, ["uuid"] = uuid, "rollcall"}, rednet.host and cluster)
clusterList[cluster] = nil
local myTimer, map = os.startTimer(5), {}
while true do
local event, par1, par2 = os.pullEvent()
if event == "timer" and par1 == myTimer then
break
elseif event == "rednet_message" and type(par2) == "table" and par2.cluster == cluster and par2.uuid == uuid and par2[1] == "rollcallResponse" then
map[#map + 1] = par1
end
end
if #map == 0 then error("bbpack.mount: Can't connect to cluster: " .. cluster, 2) end
clusterList[cluster] = map
end
local file = rfs.open(".bbpack.cfg", "w")
file.write(textutils.serialize(configTable))
file.close()
return true
end
end