mirror of
https://github.com/LDDestroier/CC/
synced 2024-11-08 19:09:58 +00:00
1235 lines
32 KiB
Lua
1235 lines
32 KiB
Lua
--[[
|
|
Progdor 2.0 - File Packaging
|
|
by LDDestroier
|
|
Get with:
|
|
wget https://raw.githubusercontent.com/LDDestroier/CC/master/progdor2.lua
|
|
|
|
Uses CCA compression API, made by minizbot2012.
|
|
--]]
|
|
|
|
local progdor = {
|
|
version = "2.0",
|
|
PBlogPath = ".progdor_PB_uploads",
|
|
channel = 8366,
|
|
skynetPath = "skynet.lua",
|
|
skynetURL = "https://github.com/osmarks/skynet/raw/master/client.lua"
|
|
}
|
|
|
|
local scr_x, scr_y = term.getSize()
|
|
local modem = peripheral.find("modem")
|
|
local skynet, skynetBigReceive, skynetBigSend
|
|
|
|
local function interpretArgs(tInput, tArgs)
|
|
local output = {}
|
|
local errors = {}
|
|
local usedEntries = {}
|
|
for aName, aType in pairs(tArgs) do
|
|
output[aName] = false
|
|
for i = 1, #tInput do
|
|
if not usedEntries[i] then
|
|
if tInput[i] == aName and not output[aName] then
|
|
if aType then
|
|
usedEntries[i] = true
|
|
if type(tInput[i+1]) == aType or type(tonumber(tInput[i+1])) == aType then
|
|
usedEntries[i+1] = true
|
|
if aType == "number" then
|
|
output[aName] = tonumber(tInput[i+1])
|
|
else
|
|
output[aName] = tInput[i+1]
|
|
end
|
|
else
|
|
output[aName] = nil
|
|
errors[1] = errors[1] and (errors[1] + 1) or 1
|
|
errors[aName] = "expected " .. aType .. ", got " .. type(tInput[i+1])
|
|
end
|
|
else
|
|
usedEntries[i] = true
|
|
output[aName] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for i = 1, #tInput do
|
|
if not usedEntries[i] then
|
|
output[#output+1] = tInput[i]
|
|
end
|
|
end
|
|
return output, errors
|
|
end
|
|
|
|
local yield = function()
|
|
os.queueEvent("yield")
|
|
os.pullEvent("yield")
|
|
end
|
|
|
|
-- CCA API START --
|
|
|
|
local bit = bit32
|
|
local function pack(bn1, bn2)
|
|
return bit.band(bn1, 0xFF), bit.rshift(bn1, 8) + bit.lshift(bit.band(bn2, 0xF), 4), bit.rshift(bn2, 4)
|
|
end
|
|
local function upack(b1, b2, b3)
|
|
return (b1 + bit.lshift(bit.band(b2, 0xF), 8)), (bit.lshift(b3,4) + bit.band(bit.rshift(b2, 4), 0xF))
|
|
end
|
|
local function createDict(bool)
|
|
local ret = {}
|
|
for i = 1, 255 do
|
|
if bool then
|
|
ret[string.char(i)] = i
|
|
else
|
|
ret[i] = string.char(i)
|
|
end
|
|
end
|
|
if not bool then ret[256] = 256 end
|
|
return ret
|
|
end
|
|
local function cp(sInput)
|
|
local dic = createDict(true)
|
|
local s = ""
|
|
local ch
|
|
local dlen = 256
|
|
local result = {}
|
|
local temp
|
|
for i = 1, #sInput do
|
|
if dlen == 4095 then
|
|
result[#result + 1] = dic[s]
|
|
result[#result + 1] = 256
|
|
dic = createDict(true)
|
|
dlen = 256
|
|
s = ""
|
|
end
|
|
ch = sInput:sub(i, i)
|
|
temp = s..ch
|
|
if dic[temp] then
|
|
s = temp
|
|
else
|
|
result[#result + 1] = dic[s]
|
|
dlen = dlen +1
|
|
dic[temp] = dlen
|
|
s = ch
|
|
end
|
|
end
|
|
result[#result + 1] = dic[s]
|
|
|
|
return result
|
|
end
|
|
local function dc(data)
|
|
local dic = createDict(false)
|
|
local entry
|
|
local ch
|
|
local currCode
|
|
local result = {}
|
|
result[#result + 1] = dic[data[1]]
|
|
prefix = dic[data[1]]
|
|
for i = 2, #data do
|
|
currCode = data[i]
|
|
if currCode == 256 then
|
|
dic = createDict(false)
|
|
prefix = ""
|
|
else
|
|
entry = dic[currCode]
|
|
if entry then -- exists in dictionary
|
|
ch = entry:sub(1, 1)
|
|
result[#result + 1] = entry
|
|
if prefix ~= "" then
|
|
dic[#dic+1] = prefix .. ch
|
|
end
|
|
else
|
|
ch = prefix:sub(1, 1)
|
|
result[#result + 1] = prefix..ch
|
|
dic[#dic + 1] = prefix..ch
|
|
end
|
|
|
|
prefix = dic[currCode]
|
|
end
|
|
end
|
|
|
|
return table.concat(result)
|
|
end
|
|
local function trim(inp)
|
|
for i = 0,2 do
|
|
if inp[#inp] == 0 then
|
|
inp[#inp] = nil
|
|
end
|
|
end
|
|
end
|
|
local function decompress(input)
|
|
local rec = {}
|
|
for i = 1, #input, 3 do
|
|
if i % 66 == 0 then
|
|
yield()
|
|
end
|
|
rec[#rec+1], rec[#rec+2] = upack(input[i], input[i+1] or 0, input[i+2] or 0)
|
|
end
|
|
trim(rec)
|
|
return dc(rec)
|
|
end
|
|
local function compress(input)
|
|
local rec = {}
|
|
local data = cp(input)
|
|
for i=1, #data, 2 do
|
|
yield()
|
|
rec[#rec+1], rec[#rec+2], rec[#rec+3] = pack(data[i], data[i+1] or 0)
|
|
end
|
|
trim(rec)
|
|
return rec
|
|
end
|
|
local function strCompress(input)
|
|
local output = {}
|
|
local tbl = compress(input)
|
|
for i = 1, #tbl do
|
|
output[i] = string.char(tbl[i])
|
|
end
|
|
return table.concat(output)
|
|
end
|
|
local function strDecompress(input)
|
|
local output = {}
|
|
for i = 1, #input do
|
|
output[i] = string.byte(input:sub(i,i))
|
|
end
|
|
return decompress(output)
|
|
end
|
|
-- CCA API END --
|
|
|
|
-- colors that are always safe to set to
|
|
local safeColorList = {
|
|
[colors.white] = true,
|
|
[colors.lightGray] = true,
|
|
[colors.gray] = true,
|
|
[colors.black] = true
|
|
}
|
|
|
|
-- pastebin uploads have a 512K limit
|
|
local pastebinFileSizeLimit = 1024 * 512
|
|
|
|
local argData = {
|
|
["-pb"] = "string", -- pastebin get
|
|
["-dd"] = "string", -- direct URL download
|
|
["-m"] = "string", -- specify main file
|
|
["-PB"] = false, -- pastebin upload
|
|
["-t"] = false, -- transmit file
|
|
["-r"] = false, -- receive file
|
|
["-R"] = false, -- include read-only files
|
|
["-P"] = false, -- include Progdor2 file
|
|
["-S"] = false, -- use skynet
|
|
["-e"] = false, -- automatic self-extractor
|
|
["-E"] = "string", -- specify output folder in self-extractor code
|
|
["-s"] = false, -- silent
|
|
["-a"] = false, -- use as API with require, also makes silent
|
|
["-c"] = false, -- use CCA compression
|
|
["-h"] = false, -- show help
|
|
["-i"] = false, -- inspect mode
|
|
["-o"] = false, -- always overwrite
|
|
}
|
|
|
|
local argList, argErrors = interpretArgs({...}, argData)
|
|
|
|
if #argErrors > 0 then
|
|
local errList = ""
|
|
for k,v in pairs(argErrors) do
|
|
if k ~= 1 then
|
|
errList = errList .. "\"" .. k .. "\": " .. v .. "; "
|
|
end
|
|
error(errList:sub(1, -2))
|
|
end
|
|
end
|
|
|
|
local pastebinGet = argList["-pb"] -- string, pastebin code
|
|
local directDownload = argList["-dd"] -- string, download URL
|
|
local mainFile = argList["-m"] -- string, main executable file
|
|
local pastebinUpload = argList["-PB"] -- boolean
|
|
local selfExtractor = argList["-e"] -- boolean
|
|
local selfExtractorFolder = argList["-E"] -- string, folder output for self extractor code
|
|
local silent = argList["-s"] -- boolean
|
|
local useCompression = argList["-c"] -- boolean
|
|
local justOverwrite = argList["-o"] -- boolean
|
|
local allowReadOnly = argList["-R"] -- boolean
|
|
local allowPackPD = argList["-P"] -- boolean
|
|
local useSkynet = argList["-S"] -- boolean
|
|
local trMode = argList["-t"] and "transmit" or (argList["-r"] and "receive" or "normal")
|
|
|
|
local skynet
|
|
|
|
if useCompression and selfExtract then
|
|
error("Cannot use compression with self-extractor.")
|
|
end
|
|
|
|
local sWrite = function(text)
|
|
if not silent then
|
|
return write(text)
|
|
end
|
|
end
|
|
|
|
local sPrint = function(text)
|
|
if not silent then
|
|
return print(text)
|
|
end
|
|
end
|
|
|
|
local cWrite = function(text, color, ignoreSilent)
|
|
local col = term.getTextColor()
|
|
term.setTextColor(color or col)
|
|
if ignoreSilent then
|
|
write(text)
|
|
else
|
|
sWrite(text)
|
|
end
|
|
term.setTextColor(col)
|
|
end
|
|
|
|
local cPrint = function(text, color, ignoreSilent)
|
|
local col = term.getTextColor()
|
|
term.setTextColor(color or col)
|
|
if ignoreSilent then
|
|
print(text)
|
|
else
|
|
sPrint(text)
|
|
end
|
|
term.setTextColor(col)
|
|
end
|
|
|
|
local function showHelp(verboseHelp)
|
|
local helpInfo
|
|
if verboseHelp then
|
|
helpInfo = {
|
|
"Progdor v" .. progdor.version,
|
|
" -pb [pastebin ID] : Download from Pastebin.", -- added
|
|
" -PB : Upload to pastebin.", -- added
|
|
" -dd [download URL] : Download from URL.", -- added
|
|
" -e : Adds on self-extractor code to archive.", -- added
|
|
" -E [folder] : Extractor extracts to folder", -- added
|
|
" -s : Silences all terminal writing", -- added
|
|
" -S : Use skynet when transmitting/receiving.", -- added
|
|
" -t : Transmit a folder/file.", -- added
|
|
" -r : Receive a file/packed folder.", -- added
|
|
" -R : Allow packing read-only files/folders.", -- added
|
|
" -P : Allow packing in Progdor2 itself.", -- added
|
|
" -a : Allows programs to use require() on Progdor.", -- added
|
|
" -c : Enables CCA compression.", -- added
|
|
" -m : Specify main executable file in archive.", -- added
|
|
" -i : Inspect archive without extracting.", -- added
|
|
" -o : Overwrite files without asking.", -- added
|
|
}
|
|
else
|
|
helpInfo = {
|
|
"Progdor v" .. progdor.version,
|
|
"Usage: progdor [options] inputFolder (outputFile)",
|
|
" progdor [options] inputFile (outputFolder)",
|
|
"",
|
|
"Progdor is a file/folder packaging program with support for CCA compression and self-extraction.",
|
|
" If tacking on auto-extractor, a third argument will be the default extraction path.",
|
|
"",
|
|
"Use -h for all options.",
|
|
"",
|
|
" This Progdor has Super Cow Powers." -- not actually added
|
|
}
|
|
end
|
|
for y = 1, #helpInfo do
|
|
sPrint(helpInfo[y])
|
|
end
|
|
end
|
|
|
|
local setTextColor = function(color)
|
|
if (not silent) and (term.isColor() or safeColorList[color]) then
|
|
term.setTextColor(color)
|
|
end
|
|
end
|
|
|
|
local setBackgroundColor = function(color)
|
|
if (not silent) and (term.isColor() or safeColorList[color]) then
|
|
term.setBackgroundColor(color)
|
|
end
|
|
end
|
|
|
|
local inputPath = argList[1]
|
|
local outputPath = argList[2] or inputPath
|
|
local defaultAutoExtractPath = argList[3]
|
|
local exists, mode
|
|
|
|
if inputPath == "moo" and not fs.exists(inputPath) then
|
|
print([[
|
|
\_/
|
|
m00h (__) -(_)-
|
|
\ ~Oo~___ / \
|
|
(..) |\
|
|
___________|_|_|_____________
|
|
..."Have you mooed today?"..."]])
|
|
return
|
|
end
|
|
|
|
if argList["-h"] then
|
|
return showHelp(true)
|
|
elseif argList["-a"] or (not shell) then
|
|
mode = "api"
|
|
elseif inputPath then
|
|
exists = fs.exists(inputPath)
|
|
if argList["-i"] then
|
|
mode = "inspect"
|
|
elseif fs.isDir(inputPath) then
|
|
mode = "pack"
|
|
else
|
|
mode = "unpack"
|
|
end
|
|
elseif trMode ~= "receive" then
|
|
return showHelp(false)
|
|
end
|
|
|
|
if mode == "api" then
|
|
silent = true
|
|
elseif (pastebinGet or directDownload) and pastebinUpload then
|
|
error("Cannot upload and download at the same time!")
|
|
end
|
|
|
|
local specialWrite = function(left, colored, right, color)
|
|
local origTextColor = term.getTextColor()
|
|
sWrite(left)
|
|
setTextColor(color)
|
|
sWrite(colored)
|
|
setTextColor(origTextColor)
|
|
sWrite(right)
|
|
end
|
|
|
|
local specialPrint = function(left, colored, right, color)
|
|
return specialWrite(left, colored, right .. "\n", color)
|
|
end
|
|
|
|
local function listAll(path, includePath)
|
|
local output = {}
|
|
local list = fs.list(path)
|
|
local fc = fs.combine
|
|
for i = 1, #list do
|
|
if allowReadOnly or (not fs.isReadOnly(fc(path, list[i]))) then
|
|
if allowPackPD or fc(path, list[i]) ~= (shell and shell.getRunningProgram()) then
|
|
if fs.isDir(fc(path, list[i])) then
|
|
if #fs.list(fc(path, list[i])) == 0 then
|
|
output[#output+1] = (includePath and fc(path, list[i]) or list[i]) .. "/"
|
|
else
|
|
local la = listAll(fc(path, list[i]))
|
|
for ii = 1, #la do
|
|
output[#output+1] = includePath and fc(path, fc(list[i], la[ii])) or fc(list[i], la[ii])
|
|
end
|
|
end
|
|
else
|
|
output[#output+1] = includePath and fc(path, list[i]) or list[i]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return output
|
|
end
|
|
|
|
local makeFileList = function(path, doCompress)
|
|
local output = {}
|
|
local list = listAll(path, false)
|
|
local file
|
|
if not allowPackPD then
|
|
cPrint("Ignoring Progdor2.", colors.lightGray)
|
|
end
|
|
if not allowReadOnly then
|
|
cPrint("Ignoring read-only files.", colors.lightGray)
|
|
end
|
|
sPrint("Packing files...")
|
|
for i = 1, #list do
|
|
setTextColor(colors.lightGray)
|
|
sWrite("'" .. list[i] .. "'...")
|
|
if list[i]:sub(-1,-1) == "/" then
|
|
output[list[i]] = true -- indicates empty directory
|
|
else
|
|
file = fs.open(fs.combine(path, list[i]), "r")
|
|
output[list[i]] = doCompress and strCompress(file.readAll()) or file.readAll()
|
|
file.close()
|
|
setTextColor(colors.green)
|
|
sPrint("good")
|
|
end
|
|
end
|
|
setTextColor(colors.white)
|
|
return output
|
|
end
|
|
|
|
local buildArchive = function(path, mainFile, doCompress)
|
|
local output = {
|
|
compressed = doCompress, -- uses CCA compression
|
|
mainFile = mainFile, -- specifies the main program within the archive to run, should I implement something to use that
|
|
data = makeFileList(path, doCompress) -- files and folders and whatnot
|
|
}
|
|
return textutils.serialize(output)
|
|
end
|
|
|
|
local parseArchiveData = function(input, doNotDecompress)
|
|
local archive = textutils.unserialize(input)
|
|
if archive then
|
|
if archive.compressed and (not doNotDecompress) then
|
|
for name, contents in pairs(archive.data) do
|
|
archive.data[name] = strDecompress(contents)
|
|
end
|
|
archive.compressed = false
|
|
end
|
|
return archive
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local parseArchive = function(path, doNotDecompress)
|
|
local file = fs.open(path, "r")
|
|
local output = parseArchiveData(file.readAll(), doNotDecompress)
|
|
file.close()
|
|
return output
|
|
end
|
|
|
|
local round = function(number, places)
|
|
return math.floor(number * (10^places)) / (10^places)
|
|
end
|
|
|
|
local choice = function(input,verbose)
|
|
if not input then
|
|
input = "yn"
|
|
end
|
|
if verbose then
|
|
sWrite("[")
|
|
for a = 1, #input do
|
|
sWrite(input:sub(a,a):upper())
|
|
if a < #input then
|
|
sWrite(",")
|
|
end
|
|
end
|
|
sWrite("]?")
|
|
end
|
|
local evt,char
|
|
repeat
|
|
evt,char = os.pullEvent("char")
|
|
until string.find(input:lower(),char:lower())
|
|
if verbose then
|
|
sPrint(char:upper())
|
|
end
|
|
local pos = string.find(input:lower(), char:lower())
|
|
return pos, char:lower()
|
|
end
|
|
|
|
local overwriteOutputPath = function(inputPath, outputPath, allowMerge, override)
|
|
setTextColor(colors.white)
|
|
local c
|
|
if override then
|
|
return true, true
|
|
else
|
|
if allowMerge then
|
|
write("Overwrite [Y/N]? Or [M]erge? ")
|
|
c = choice("ynm", false)
|
|
else
|
|
write("Overwrite [Y/N]?")
|
|
c = choice("yn", false)
|
|
end
|
|
write("\n")
|
|
if c == 1 then
|
|
return true, true
|
|
elseif c == 2 then
|
|
sPrint("Abort.")
|
|
return false, false
|
|
elseif c == 3 then
|
|
return true, false
|
|
end
|
|
end
|
|
end
|
|
|
|
local uploadToPastebin = function(archive, name)
|
|
if #archive > pastebinFileSizeLimit then
|
|
error("That archive is too large to be uploaded to Pastebin. (limit is 512 KB)")
|
|
return false
|
|
else
|
|
local key = "0ec2eb25b6166c0c27a394ae118ad829"
|
|
local response = http.post(
|
|
"https://pastebin.com/api/api_post.php",
|
|
"api_option=paste&" ..
|
|
"api_dev_key=" .. key .. "&" ..
|
|
"api_paste_format=lua&" ..
|
|
"api_paste_name=" .. textutils.urlEncode(name) .. "&" ..
|
|
"api_paste_code=" .. textutils.urlEncode(archive)
|
|
)
|
|
if response then
|
|
local sResponse = response.readAll()
|
|
response.close()
|
|
|
|
local sCode = string.match( sResponse, "[^/]+$" )
|
|
return sCode, sResponse
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
local writeArchiveData = function(archive, outputPath)
|
|
local file
|
|
for name, contents in pairs(archive.data) do
|
|
setTextColor(colors.lightGray)
|
|
sWrite("'" .. name .. "'...")
|
|
if contents == true then -- indicates empty directory
|
|
fs.makeDir(fs.combine(outputPath, name))
|
|
else
|
|
file = fs.open(fs.combine(outputPath, name), "w")
|
|
if file then
|
|
file.write(contents)
|
|
file.close()
|
|
end
|
|
end
|
|
if file then
|
|
setTextColor(colors.green)
|
|
sPrint("good")
|
|
else
|
|
setTextColor(colors.red)
|
|
sPrint("fail")
|
|
end
|
|
end
|
|
setTextColor(colors.white)
|
|
specialPrint("Unpacked to '", outputPath .. "/", "'.", colors.yellow)
|
|
end
|
|
|
|
local getSkynet = function()
|
|
if http.websocket then
|
|
-- Skynet only supports messages that are 65506 bytes or smaller
|
|
-- I'm just going with 65200 bytes to play safe.
|
|
local defineBigOnes = function(skynet)
|
|
local div = 65200
|
|
return function(channel, _message) -- big send
|
|
local message = textutils.serialize(_message)
|
|
for i = 1, math.ceil(#message / div) do
|
|
skynet.send(progdor.channel, {
|
|
msg = message:sub( (i - 1) * div + 1, i * div ),
|
|
complete = i == math.ceil(#message / div),
|
|
part = i
|
|
})
|
|
sleep(0.1)
|
|
cWrite(".", colors.lightGray)
|
|
end
|
|
end, function(channel) -- big receive
|
|
local ch, msg
|
|
local output = {}
|
|
local gotFile = false
|
|
while true do
|
|
ch, msg = skynet.receive(channel)
|
|
if type(msg) == "table" then
|
|
if type(msg.complete) == "boolean" and type(msg.msg) == "string" and type(msg.part) == "number" then
|
|
output[msg.part] = msg.msg
|
|
cWrite(".", colors.lightGray)
|
|
if msg.complete then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return channel, textutils.unserialize(table.concat(output))
|
|
end
|
|
end
|
|
if skynet then
|
|
local bS, bR = defineBigOnes(skynet)
|
|
skynet.open(progdor.channel)
|
|
return skynet, "", bS, bR
|
|
else
|
|
if fs.exists(progdor.skynetPath) then
|
|
local sn = dofile(progdor.skynetPath)
|
|
sn.open(progdor.channel)
|
|
local bS, bR = defineBigOnes(sn)
|
|
return sn, "", bS, bR
|
|
else
|
|
local net, contents = http.get(progdor.skynetURL)
|
|
if net then
|
|
contents = net.readAll()
|
|
local file = fs.open(progdor.skynetPath, "w")
|
|
file.write(contents)
|
|
file.close()
|
|
local sn = dofile(progdor.skynetPath)
|
|
local bS, bR = defineBigOnes(sn)
|
|
sn.open(progdor.channel)
|
|
return sn, "", bS, bR
|
|
else
|
|
return false, "Couldn't download Skynet."
|
|
end
|
|
end
|
|
end
|
|
else
|
|
return false, "This version of CC does not support Skynet."
|
|
end
|
|
end
|
|
|
|
local getModem = function()
|
|
local mod = peripheral.find("modem")
|
|
if mod then
|
|
mod.open(progdor.channel)
|
|
return mod
|
|
else
|
|
return false, "No modem was found."
|
|
end
|
|
end
|
|
|
|
local archive
|
|
local doOverwrite, doContinue = false, true
|
|
|
|
--[[ JUST SUMMIN' UP THE ELSEIF CHAIN
|
|
if mode == "api" then
|
|
elseif trMode == "transmit" then
|
|
if mode == "pack" then
|
|
end
|
|
elseif trMode == "receive" then
|
|
if mode == "pack" then
|
|
end
|
|
elseif mode == "pack" then
|
|
elseif mode == "unpack" then
|
|
elseif mode == "inspect" then
|
|
end
|
|
--]]
|
|
|
|
-- API mode takes top priority
|
|
if mode == "api" then
|
|
|
|
return {
|
|
parseArchive = parseArchive,
|
|
parseArchiveData = parseArchiveData,
|
|
buildArchive = buildArchive,
|
|
uploadToPastebin = uploadToPastebin,
|
|
}
|
|
|
|
-- after that, trans
|
|
elseif trMode == "transmit" then
|
|
|
|
-- assemble something to send
|
|
local output = {name = fs.getName(inputPath)}
|
|
if mode == "pack" then
|
|
output.contents = textutils.serialize(buildArchive(inputPath, mainFile, useCompression))
|
|
else
|
|
local file = fs.open(inputPath, "r")
|
|
output.contents = file.readAll()
|
|
file.close()
|
|
end
|
|
|
|
local grr
|
|
if useSkynet then
|
|
if not skynet then
|
|
cWrite("Connecting to Skynet...", colors.lightGray)
|
|
skynet, grr, skynetBigSend, skynetBigReceive = getSkynet()
|
|
if not skynet then
|
|
print(grr)
|
|
print("Aborting.")
|
|
return false
|
|
else
|
|
cPrint("good", colors.green)
|
|
end
|
|
end
|
|
cWrite("Sending file...", colors.lightGray)
|
|
skynetBigSend(progdor.channel, output)
|
|
skynet.socket.close()
|
|
cPrint("good", colors.green)
|
|
sWrite("Sent '")
|
|
cWrite(fs.getName(inputPath), colors.yellow)
|
|
sPrint("' using Skynet.")
|
|
else
|
|
modem, grr = getModem()
|
|
if not modem then
|
|
print(grr)
|
|
print("Abort.")
|
|
return false
|
|
end
|
|
cWrite("Sending file...", colors.lightGray)
|
|
modem.transmit(progdor.channel, progdor.channel, output)
|
|
cPrint("good", colors.green)
|
|
sWrite("Sent '")
|
|
cWrite(fs.getName(inputPath), colors.yellow)
|
|
sPrint("' using modem.")
|
|
end
|
|
|
|
elseif trMode == "receive" then
|
|
local grr
|
|
local gotFile = false
|
|
local input, channel
|
|
local didAbort = false
|
|
if useSkynet then
|
|
if not skynet then
|
|
cWrite("Connecting to Skynet...", colors.lightGray)
|
|
skynet, grr, skynetBigSend, skynetBigReceive = getSkynet()
|
|
if not skynet then
|
|
print(grr)
|
|
print("Aborting.")
|
|
return false
|
|
else
|
|
cPrint("good", colors.green)
|
|
end
|
|
end
|
|
cWrite("Waiting for file on Skynet...", colors.lightGray)
|
|
local result, grr = pcall(function()
|
|
sleep(0.05)
|
|
while true do
|
|
if parallel.waitForAny(function()
|
|
channel, input = skynetBigReceive(progdor.channel)
|
|
end, function()
|
|
local evt
|
|
while true do
|
|
evt = {os.pullEvent()}
|
|
if evt[1] == "key" then
|
|
if evt[2] == keys.q then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end) == 2 then
|
|
print("\nAbort.")
|
|
sleep(0.05)
|
|
didAbort = true
|
|
break
|
|
end
|
|
if channel == progdor.channel and type(input) == "table" then
|
|
if type(input.contents) == "string" and type(input.name) == "string" then
|
|
gotFile = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
skynet.socket.close()
|
|
if not result then
|
|
error(grr, 0)
|
|
end
|
|
else
|
|
modem, grr = getModem()
|
|
if not modem then
|
|
print(grr)
|
|
print("Abort.")
|
|
sleep(0.05)
|
|
return false
|
|
end
|
|
modem.open(progdor.channel)
|
|
local evt
|
|
cWrite("Waiting for file...", colors.lightGray)
|
|
sleep(0.05)
|
|
while true do
|
|
evt = {os.pullEvent()}
|
|
if evt[1] == "modem_message" then
|
|
if evt[3] == progdor.channel and type(evt[5]) == "table" then
|
|
if type(evt[5].contents) == "string" and type(evt[5].name) == "string" then
|
|
input = evt[5]
|
|
gotFile = true
|
|
break
|
|
end
|
|
end
|
|
elseif evt[1] == "key" then
|
|
if evt[2] == keys.q then
|
|
print("\nAbort.")
|
|
sleep(0.05)
|
|
didAbort = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if gotFile then
|
|
cPrint("good", colors.green)
|
|
if input.contents then
|
|
local writePath, c = fs.combine(shell.dir(), outputPath or input.name)
|
|
write("Received '")
|
|
cWrite(input.name or outputPath, colors.yellow, true)
|
|
print("'.")
|
|
if (not justOverwrite and fs.exists(writePath)) or fs.isReadOnly(writePath) then
|
|
write("\nBut, '")
|
|
cWrite(fs.getName(writePath), colors.yellow, true)
|
|
print("' is already there.")
|
|
local roCount = 0
|
|
local showROmessage = function(roCount)
|
|
if roCount == 1 then
|
|
write("\nThat file/folder is ")
|
|
cWrite("read-only", colors.yellow, true)
|
|
print("!")
|
|
elseif roCount == 2 then
|
|
write("\nI told you, that file/folder is ")
|
|
cWrite("read-only", colors.yellow, true)
|
|
print("!")
|
|
elseif roCount == 3 then
|
|
write("\nNope. The file/folder is ")
|
|
cWrite("read-only", colors.yellow, true)
|
|
print(".")
|
|
elseif roCount == 4 then
|
|
write("\nDoes the phrase ")
|
|
cWrite("read-only", colors.yellow, true)
|
|
print(" mean nothing to you?")
|
|
elseif roCount == 5 then
|
|
print("\nAlright wise-ass, that's enough.")
|
|
elseif roCount > 5 then
|
|
write("\nThat's ")
|
|
cWrite("read-only", colors.yellow, true)
|
|
print(", damn you!")
|
|
end
|
|
end
|
|
while true do
|
|
sleep(0.05)
|
|
if roCount < 5 then
|
|
write("Overwrite [Y/N]? Or [R]ename?\n")
|
|
c = choice("nry", false)
|
|
else
|
|
write("Overwrite [ /N]? Or [R]ename?\n")
|
|
c = choice("nr", false)
|
|
end
|
|
if c == 3 then
|
|
if fs.isReadOnly(writePath) then
|
|
roCount = roCount + 1
|
|
showROmessage(roCount)
|
|
else
|
|
break
|
|
end
|
|
elseif c == 1 then
|
|
print("Abort.")
|
|
return false
|
|
elseif c == 2 then
|
|
print("New name:")
|
|
if shell.dir() == "" then
|
|
write("/")
|
|
else
|
|
write("/" .. shell.dir() .. "/")
|
|
end
|
|
writePath = fs.combine(shell.dir(), read())
|
|
roCount = roCount + 1
|
|
if fs.isReadOnly(writePath) then
|
|
showROmessage(roCount)
|
|
else
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local file = fs.open(writePath, "w")
|
|
file.write(input.contents)
|
|
file.close()
|
|
sWrite("Wrote to '")
|
|
cWrite(writePath, colors.yellow)
|
|
sPrint("'")
|
|
end
|
|
elseif not didAbort then
|
|
print("fail!")
|
|
end
|
|
|
|
elseif mode == "pack" then
|
|
|
|
if not pastebinUpload then
|
|
if fs.isReadOnly(outputPath) then
|
|
error("Output path is read-only.")
|
|
elseif fs.exists(outputPath) and (outputPath ~= inputPath) then
|
|
doContinue, doOverwrite = overwriteOutputPath(inputPath, outputPath, false, justOverwrite)
|
|
elseif fs.combine("", outputPath) == "" then
|
|
error("Output path cannot be root.")
|
|
end
|
|
if not doContinue then
|
|
return false
|
|
elseif outputPath == inputPath then
|
|
doOverwrite = true
|
|
end
|
|
end
|
|
archive = buildArchive(inputPath, mainFile, useCompression)
|
|
if exists then
|
|
if useCompression then
|
|
sPrint("Using CCA compression.")
|
|
elseif selfExtractor then
|
|
sPrint("Tacking on self-extractor.")
|
|
archive = ([[
|
|
local tArg = {...}
|
|
local selfDelete = false -- if true, deletes extractor after running
|
|
local file
|
|
local outputPath = ]] ..
|
|
|
|
(selfExtractorFolder and (
|
|
"shell.resolve(\"" .. selfExtractorFolder .. "\")"
|
|
) or (
|
|
"tArg[1] and shell.resolve(tArg[1]) or ]]" .. ((defaultAutoExtractPath and ("\"" .. defaultAutoExtractPath .. "\"")) or "shell.getRunningProgram()")
|
|
)) .. [[
|
|
|
|
local safeColorList = {[colors.white] = true,[colors.lightGray] = true,[colors.gray] = true,[colors.black] = true}
|
|
local stc = function(color) if (term.isColor() or safeColorList[color]) then term.setTextColor(color) end end
|
|
local choice = function()
|
|
local input = "yn"
|
|
write("[")
|
|
for a = 1, #input do
|
|
write(input:sub(a,a):upper())
|
|
if a < #input then
|
|
write(",")
|
|
end
|
|
end
|
|
print("]?")
|
|
local evt,char
|
|
repeat
|
|
evt,char = os.pullEvent("char")
|
|
until string.find(input:lower(),char:lower())
|
|
if verbose then
|
|
print(char:upper())
|
|
end
|
|
local pos = string.find(input:lower(), char:lower())
|
|
return pos, char:lower()
|
|
end
|
|
local archive = textutils.unserialize(]] ..
|
|
|
|
textutils.serialize(archive) ..
|
|
|
|
[[)
|
|
if fs.isReadOnly(outputPath) then
|
|
error("Output path is read-only. Abort.")
|
|
elseif fs.getFreeSpace(outputPath) <= #archive then
|
|
error("Insufficient space. Abort.")
|
|
end
|
|
|
|
]] .. ( justOverwrite and [[
|
|
if fs.exists(outputPath) and fs.combine("", outputPath) ~= "" then
|
|
fs.delete(outputPath)
|
|
end
|
|
]] or [[
|
|
if fs.exists(outputPath) and fs.combine("", outputPath) ~= "" then
|
|
print("File/folder already exists! Overwrite?")
|
|
stc(colors.lightGray)
|
|
print("(Use -o when making the extractor to always overwrite.)")
|
|
stc(colors.white)
|
|
if choice() ~= 1 then
|
|
error("Chose not to overwrite. Abort.")
|
|
else
|
|
fs.delete(outputPath)
|
|
end
|
|
end
|
|
]]
|
|
) ..
|
|
[[
|
|
if selfDelete or (fs.combine("", outputPath) == shell.getRunningProgram()) then
|
|
fs.delete(shell.getRunningProgram())
|
|
end
|
|
for name, contents in pairs(archive.data) do
|
|
stc(colors.lightGray)
|
|
write("'" .. name .. "'...")
|
|
if contents == true then -- indicates empty directory
|
|
fs.makeDir(fs.combine(outputPath, name))
|
|
else
|
|
file = fs.open(fs.combine(outputPath, name), "w")
|
|
if file then
|
|
file.write(contents)
|
|
file.close()
|
|
end
|
|
end
|
|
if file then
|
|
stc(colors.green)
|
|
print("good")
|
|
else
|
|
stc(colors.red)
|
|
print("fail")
|
|
end
|
|
end
|
|
stc(colors.white)
|
|
write("Unpacked to '")
|
|
stc(colors.yellow)
|
|
write(outputPath .. "/")
|
|
stc(colors.white)
|
|
print("'.")
|
|
]])
|
|
|
|
end
|
|
if pastebinUpload then
|
|
sWrite("Uploading to Pastebin...")
|
|
local id, url = uploadToPastebin(archive, fs.getName(inputPath))
|
|
if id then
|
|
setTextColor(colors.green)
|
|
sPrint("success!")
|
|
setTextColor(colors.white)
|
|
sPrint("Uploaded to '" .. url .. "'.")
|
|
specialPrint("Retrieve with \"", "progdor -pb " .. id .. " " .. fs.getName(inputPath), "\".", colors.yellow)
|
|
sPrint("You may need to do a Captcha on the website.")
|
|
if not fs.exists(progdor.PBlogPath) then
|
|
setTextColor(colors.lightGray)
|
|
specialPrint("(PB uploads are logged at \"", progdor.PBlogPath, "\".)", colors.yellow)
|
|
setTextColor(colors.white)
|
|
end
|
|
-- precautionary log file
|
|
local file = fs.open(progdor.PBlogPath, "a")
|
|
file.writeLine("uploaded \"" .. inputPath .. "\" to \"" .. url .. "\"")
|
|
file.close()
|
|
else
|
|
sPrint("failed!")
|
|
end
|
|
else
|
|
if doOverwrite then
|
|
fs.delete(outputPath)
|
|
end
|
|
local file = fs.open(outputPath, "w")
|
|
file.write(archive)
|
|
file.close()
|
|
if selfExtract then
|
|
specialPrint("Written self-extractor to '", outputPath, "'.", colors.yellow)
|
|
else
|
|
specialPrint("Written to '", outputPath, "'.", colors.yellow)
|
|
end
|
|
end
|
|
else
|
|
error("No such input path exists.")
|
|
return false
|
|
end
|
|
|
|
elseif mode == "unpack" then -- unpack OR upload
|
|
|
|
if pastebinUpload then
|
|
local file = fs.open(inputPath, "r")
|
|
archive = file.readAll()
|
|
file.close()
|
|
sWrite("Uploading to Pastebin...")
|
|
local id, url = uploadToPastebin(archive, fs.getName(inputPath))
|
|
if id then
|
|
setTextColor(colors.green)
|
|
sPrint("success!")
|
|
setTextColor(colors.white)
|
|
sPrint("Uploaded to '" .. url .. "'.")
|
|
specialPrint("Retrieve with \"", "progdor -pb " .. id .. " " .. fs.getName(inputPath), "\".", colors.yellow)
|
|
sPrint("You may need to do a Captcha on the website.")
|
|
if not fs.exists(progdor.PBlogPath) then
|
|
setTextColor(colors.lightGray)
|
|
specialPrint("(PB uploads are logged at \"", progdor.PBlogPath, "\".)", colors.yellow)
|
|
setTextColor(colors.white)
|
|
end
|
|
-- precautionary log file
|
|
local file = fs.open(progdor.PBlogPath, "a")
|
|
file.writeLine("uploaded \"" .. inputPath .. "\" to \"" .. url .. "\"")
|
|
file.close()
|
|
else
|
|
setTextColor(colors.red)
|
|
sPrint("failed!")
|
|
setTextColor(colors.white)
|
|
return false
|
|
end
|
|
elseif pastebinGet or directDownload then
|
|
local url, contents
|
|
if pastebinGet and directDownload then
|
|
error("Cannot do both pastebin get and direct download.")
|
|
elseif fs.isReadOnly(outputPath) then
|
|
error("Output path is read-only.")
|
|
elseif fs.combine(outputPath, "") == "" then
|
|
error("Output path cannot be root.")
|
|
else
|
|
if pastebinGet then
|
|
url = "http://www.pastebin.com/raw/" .. pastebinGet
|
|
elseif directDownload then
|
|
url = directDownload
|
|
end
|
|
if fs.exists(outputPath) and (outputPath ~= inputPath) or outputPath == shell.getRunningProgram() then
|
|
doContinue, doOverwrite = overwriteOutputPath(inputPath, outputPath, true, justOverwrite)
|
|
end
|
|
if not doContinue then
|
|
return false
|
|
elseif outputPath == inputPath then
|
|
doOverwrite = true
|
|
end
|
|
sWrite("Connecting to \"")
|
|
setTextColor(colors.yellow)
|
|
sWrite(url)
|
|
setTextColor(colors.white)
|
|
sWrite("\"...")
|
|
local handle = http.get(url)
|
|
if handle then
|
|
cPrint("success!", colors.green)
|
|
contents = handle.readAll()
|
|
handle.close()
|
|
|
|
-- detects if you didn't solve the captcha, since archives commonly trigger anti-spam measures
|
|
if (
|
|
pastebinGet and
|
|
(not textutils.unserialize(contents)) and
|
|
contents:find("Your paste has triggered our automatic SPAM detection filter.")
|
|
) then
|
|
specialPrint("You must go to '", url, "' and do the Captcha to be able to download that paste.", colors.yellow)
|
|
return false
|
|
end
|
|
|
|
setTextColor(colors.lightGray)
|
|
sWrite("Parsing archive...")
|
|
archive = parseArchiveData(contents)
|
|
if archive then
|
|
setTextColor(colors.green)
|
|
sPrint("good")
|
|
else
|
|
setTextColor(colors.red)
|
|
sPrint("Invalid archive file.")
|
|
return false
|
|
end
|
|
if doOverwrite then
|
|
fs.delete(outputPath)
|
|
end
|
|
writeArchiveData(archive, outputPath)
|
|
else
|
|
setTextColor(colors.red)
|
|
sPrint("failed!")
|
|
setTextColor(colors.white)
|
|
return false
|
|
end
|
|
end
|
|
else -- regular unpack
|
|
if exists then
|
|
if fs.isReadOnly(outputPath) then
|
|
error("Output path is read-only.")
|
|
elseif fs.exists(outputPath) and (outputPath ~= inputPath) or outputPath == shell.getRunningProgram() then
|
|
doContinue, doOverwrite = overwriteOutputPath(inputPath, outputPath, true, justOverwrite)
|
|
end
|
|
if not doContinue then
|
|
return false
|
|
elseif outputPath == inputPath then
|
|
doOverwrite = true
|
|
end
|
|
setTextColor(colors.lightGray)
|
|
sWrite("Parsing archive...")
|
|
archive = parseArchive(inputPath)
|
|
if archive then
|
|
setTextColor(colors.green)
|
|
sPrint("good")
|
|
if doOverwrite then
|
|
fs.delete(outputPath)
|
|
end
|
|
writeArchiveData(archive, outputPath)
|
|
else
|
|
setTextColor(colors.red)
|
|
sPrint("Invalid archive file.")
|
|
return false
|
|
end
|
|
else
|
|
error("No such input path exists.")
|
|
end
|
|
end
|
|
|
|
elseif mode == "inspect" then
|
|
|
|
if exists and (not fs.isDir(inputPath)) then
|
|
archive = parseArchive(inputPath, true)
|
|
local totalSize = 0
|
|
local amountOfFiles = 0
|
|
local averageSize = 0
|
|
|
|
local output = {}
|
|
|
|
if archive then
|
|
for k,v in pairs(archive) do
|
|
if k == "data" then
|
|
for name, contents in pairs(v) do
|
|
if contents then -- don't count directories, where contents == false
|
|
totalSize = totalSize + #contents
|
|
amountOfFiles = amountOfFiles + 1
|
|
end
|
|
end
|
|
averageSize = math.ceil(totalSize / amountOfFiles)
|
|
else
|
|
output[#output + 1] = k .. " = \"" .. tostring(v) .. "\""
|
|
end
|
|
end
|
|
sPrint("# of files: " .. amountOfFiles)
|
|
sPrint("Total size: " .. totalSize .. " bytes (" .. round(totalSize / 1024, 1) .. " KB)")
|
|
sPrint("Aveg. size: " .. averageSize .. " bytes (" .. round(averageSize / 1024, 1) .. " KB)")
|
|
sPrint(("-"):rep(scr_x))
|
|
for i = 1, #output do
|
|
sPrint(output[i])
|
|
end
|
|
else
|
|
error("Invalid archive file.")
|
|
end
|
|
else
|
|
if fs.isDir(inputPath) then
|
|
error("Cannot inspect directories.")
|
|
else
|
|
error("No such input path exists.")
|
|
end
|
|
end
|
|
|
|
end
|