morganamilo

LunaOS Installer

Aug 29th, 2016
378
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.08 KB | None | 0 0
  1. local args = {...}
  2.  
  3. local gitProfile = {
  4.     api = "https://api.github.com/repos",
  5.     raw = "https://raw.githubusercontent.com",
  6.     username = "Morganamilo",
  7.     reponame = "LunaOS",
  8.     access_token = "31b6f63ef3e86f2e9972e2dae485ea6b02442bea"
  9. }
  10.  
  11. local blacklist = {
  12.     ".gitignore",
  13.     "README.md",
  14.     "docs"
  15. }
  16.  
  17. local function isBlacklisted(str)
  18.     for k,v in pairs(blacklist) do
  19.         if v == str:sub(1, #v) then
  20.             return true
  21.         end
  22.     end
  23.  
  24.     return false
  25. end
  26.  
  27. local git = {}
  28. local utils = {}
  29.  
  30. local numToHex = {"1", "2", "3", "4", "5", "6", "7", "b", "9", "a", "b", "c", "d", "e", "f"}
  31. numToHex[0] = "0"
  32. numToHex[16] = "-"
  33.  
  34. local whitespace = {" ", "\t", "\n", "\r"}
  35. local escapes = {["\\\""] = "\"", ["\\\\"] = "\\", ["\\b"] = "\b", ["\\f"] = "\f", ["\\n"] = "\n", ["\\r"] = "\r", ["\\t"] = "\t", ["\\/"] = "/"}
  36. local control = {":",",","{", "}", "[", "]"}
  37. local JSON = {}
  38. local xSize, ySize = term.getSize()
  39.  
  40. local paneSize = 9
  41. local paneStart = math.floor(ySize/2 - paneSize / 2)
  42.  
  43. local drawText
  44. local drawBackground
  45. local drawBar
  46. local drawDots
  47. local decodeImage
  48. local drawImage
  49.  
  50. local width = xSize - 6
  51.  
  52. local imageStart = paneStart
  53. local textStart = paneStart + 5
  54. local barStart = paneStart + 9
  55.  
  56. local filesToDownload = 100
  57. local filesDownloaded = 0
  58.  
  59. local image = "       "
  60.  
  61. local function drawBackground()
  62.     local backgroundPos = math.floor((xSize - width) / 2) + 1
  63.    
  64.     local whitespace = string.rep(" ", width)
  65.     local textColour = string.rep("8", width)
  66.    
  67.     for y = 5, 8 do
  68.         term.setCursorPos(backgroundPos, y)
  69.         term.blit(whitespace, textColour, textColour)
  70.     end
  71.    
  72. end
  73.  
  74. local function drawText(text)
  75.     local width = width - 2
  76.     local textPos = math.floor((xSize - width) / 2) + 1
  77.    
  78.     local textColour = string.rep("b", width)
  79.     local backgroundColour = string.rep("7", width)
  80.    
  81.     for y = textStart, textStart + 3 do
  82.         local t = text:sub(1  + (y - textStart) * width, (y - textStart + 1) * width)
  83.         local prePad = string.rep(" ", math.floor(math.max(width - #t, 0) / 2))
  84.         local postPad = string.rep(" ", width - #prePad - #t)
  85.        
  86.         t = prePad .. t .. postPad
  87.         term.setCursorPos(textPos +  width - #t , y)
  88.         term.blit(t, textColour, backgroundColour)
  89.     end
  90. end
  91.  
  92. local function drawBar(percent)
  93.     local width = width - 2
  94.     local percent = math.floor((percent * width) + 0.5)
  95.    
  96.     local backgroundPos = math.floor((xSize - width) / 2) + 1
  97.    
  98.     local whitespace = string.rep(" ", width)
  99.     local textColour = string.rep("9", percent) ..  string.rep("8", width - percent)
  100.    
  101.     term.setCursorPos(backgroundPos, barStart)
  102.     term.blit(whitespace, textColour, textColour)
  103. end
  104.  
  105. local function decodeImage(image)
  106.     local decodeImage = {size = {}, text = {}, textColour = {}, colour = {}}
  107.    
  108.     decodeImage.size[1] = string.byte(image:sub(1,1))
  109.     decodeImage.size[2] = string.byte(image:sub(2,2))
  110.    
  111.     for n = 3, #image, 3 do
  112.         local text = image:sub(n,n)
  113.         local textColour = string.byte(image, n + 1)
  114.         local colour = string.byte(image, n + 2)
  115.         local l = #decodeImage.text + 1
  116.        
  117.         decodeImage.text[l] = text
  118.         decodeImage.textColour[l] = numToHex[textColour]
  119.         decodeImage.colour[l] = numToHex[colour]
  120.     end
  121.    
  122.     return decodeImage
  123. end
  124.  
  125. local function drawImage()
  126.     for y = 1, image.size[2] do
  127.         local pixel = ""
  128.         local text = ""
  129.         local textColour = ""
  130.        
  131.         for x = 1, image.size[1] do
  132.             pixel = pixel .. image.colour[x + ((y - 1)* image.size[1])] or " "
  133.             text = text .. image.text[x + ((y - 1)* image.size[1])] or "0"
  134.             textColour = textColour .. image.textColour[x + ((y - 1)* image.size[1])] or "0"
  135.         end
  136.        
  137.         term.setCursorPos(1 + math.floor(xSize/2 - image.size[1]/2), imageStart + y - 1)
  138.         term.blit(text, textColour, pixel)
  139.     end
  140. end
  141.  
  142. function JSON.escape(str)
  143.     for k, v in pairs(escapes) do
  144.         str = str:gsub(v, k)
  145.     end
  146.    
  147.     return str
  148. end
  149.  
  150. function JSON.unescape(str)
  151.     for k, v in pairs(escapes) do
  152.         str = str:gsub(k, v)
  153.     end
  154.    
  155.     return str
  156. end
  157.  
  158. function JSON.isArray(tbl)
  159.     local size = 0
  160.    
  161.     for k in pairs(tbl) do
  162.         if type(k) ~= "number" then return false end
  163.         size = math.max(k, size)
  164.     end
  165.    
  166.     return size
  167. end
  168.  
  169. function JSON.valueToJson(value, indentLevel)
  170.     local t = type(value)
  171.            
  172.     if t == "string" then
  173.         return '"' .. JSON.escape(value) .. '"'
  174.     elseif t == "number" then
  175.         return value
  176.     elseif t == "boolean" then
  177.         return tostring(value)
  178.     elseif t == 'table' then
  179.         if indentLevel  then indentLevel = indentLevel + 2 end
  180.         return JSON.encodeInternl(value, indentLevel)
  181.     elseif t == "nil" then
  182.         return "null"
  183.     else
  184.         error("Error: Unserializable type: " .. t, 0)
  185.     end
  186. end
  187.  
  188. function JSON.keyToJson(key)
  189.     if type(key) == "string" then
  190.         return '"' .. JSON.escape(key) .. '"'
  191.     else
  192.         error("Error: Key must be string, got " .. type(key), 0)
  193.     end
  194. end
  195.  
  196. function JSON.encodeInternl(tbl, indentLevel)
  197.     assert(type(tbl) == "table", "Error: Table expected got " .. type(tbl), 2)
  198.     local isA = JSON.isArray(tbl)
  199.     local jsonStr = (isA and "[" or "{")
  200.     local indent
  201.    
  202.     if indentLevel then
  203.         indent = string.rep("\t", indentLevel + 1)
  204.     end
  205.    
  206.     if isA then
  207.         for i = 1, isA do
  208.             if indentLevel then jsonStr = jsonStr.. "\n ".. indent end
  209.             jsonStr = jsonStr .. JSON.valueToJson(tbl[i], indentLevel) .. ","
  210.         end
  211.     else
  212.         for k,v in pairs(tbl) do
  213.             if indentLevel then jsonStr =  jsonStr .. "\n ".. indent end
  214.             jsonStr =  jsonStr .. JSON.keyToJson(k) .. ":" .. JSON.valueToJson(v, indentLevel) .. ","
  215.         end
  216.     end
  217.        
  218.     if isA ~= 0 then jsonStr = jsonStr:sub(1,#jsonStr - 1) end --remove last comma
  219.    
  220.     if indentLevel then jsonStr = jsonStr .. "\n" .. string.rep("\t", indentLevel) end
  221.     return jsonStr ..(isA and "]" or "}"), indentLevel
  222. end
  223.  
  224. function JSON.encode(tbl, readable)
  225.     local encoded = JSON.encodeInternl(tbl, readable and 0 or nil)
  226.     return encoded
  227. end
  228.  
  229. -------------------------------------------------------------
  230.  
  231. function JSON.nextValue(str, start)
  232.     local isIn = utils.indexOf
  233.    
  234.     for pos = start, #str do
  235.         local c = str:sub(pos, pos)
  236.        
  237.         if not isIn(whitespace, c) then
  238.             return c, pos
  239.         end
  240.     end
  241.    
  242.     error("Error: Unexpected end of input near" .. start, 0)
  243. end
  244.  
  245. function JSON.parseArray(str, start)
  246.     local array = {}
  247.     local counter = 1
  248.    
  249.     local closeChar, closePos = JSON.nextValue(str, start + 1)
  250.     if closeChar == "]" then
  251.         closeChar, closePos = JSON.nextValue(str, closePos + 1)
  252.         return array, closePos
  253.     end
  254.    
  255.     while true do
  256.         local c
  257.        
  258.         c, start = JSON.nextValue(str, start + 1)
  259.         array[counter], start = JSON.parseValue(str, start)
  260.         counter = counter + 1
  261.         c, start = JSON.nextValue(str, start)
  262.        
  263.         if c == "]" then
  264.             return array, start + 1
  265.         elseif c ~= "," then
  266.             error("Error: Expected ',' near" .. start, 0)
  267.         end
  268.     end
  269. end
  270.  
  271. function JSON.parseObject(str, start)
  272.     local c
  273.     local object = {}
  274.    
  275.     if JSON.nextValue(str, start + 1) == "}" then return object, start + 2 end
  276.    
  277.     while true do
  278.         local k, v
  279.        
  280.         c, start = JSON.nextValue(str, start + 1)
  281.        
  282.         k, v, start = JSON.parsePair(str, start)
  283.         object[k] = v
  284.        
  285.         c, start = JSON.nextValue(str, start)
  286.        
  287.         if c == "}" then
  288.             return object, start + 1
  289.         elseif c ~= "," then
  290.             error("Error: Expected ',' near " .. start, 0)
  291.         end
  292.     end
  293. end
  294.  
  295. function JSON.parsePair(str, start)
  296.     local key
  297.     local val
  298.     local c
  299.    
  300.     key, start = JSON.parseValue(str, start)
  301.     c, start = JSON.nextValue(str, start)
  302.     assert(type(key) == "string", "Error: key must be string near" .. start, 0)
  303.    
  304.     if c ~= ":" then
  305.         error("Error: Expected ':' near " .. start, 0)
  306.     else
  307.         c, start = JSON.nextValue(str, start + 1)
  308.     end
  309.    
  310.     value, start = JSON.parseValue(str, start)
  311.    
  312.     return key, value, start
  313. end
  314.  
  315. function JSON.parseValue(str, start)
  316.     if str:sub(start, start) == "\"" then
  317.         return JSON.parseString(str, start + 1)
  318.     else
  319.         return JSON.parseNonString(str, start)
  320.     end
  321. end
  322.  
  323. function JSON.parseString(str, start)
  324.     for pos = start, #str do
  325.         local c = str:sub(pos, pos)
  326.        
  327.         if c == "\"" then
  328.             local isEscped = false
  329.            
  330.             for p = pos - 1, start +1, -1 do
  331.                 if str:sub(p,p) == "\\" then  isEscped = not isEscped
  332.                 else break end
  333.             end
  334.            
  335.             if not isEscped then
  336.                 return JSON.unescape(str:sub(start, pos - 1)), pos + 1
  337.             end
  338.            
  339.         end
  340.     end
  341. end
  342.  
  343. function JSON.parseNonString(str, start)
  344.     local endPos
  345.     local isIn = utils.indexOf
  346.     local prevC
  347.    
  348.     if str:sub(start, start) == "{" then
  349.         return JSON.parseObject(str, start)
  350.     elseif str:sub(start, start) == "[" then
  351.         return JSON.parseArray(str, start)
  352.     end
  353.    
  354.     for pos = start, #str do
  355.         local c = str:sub(pos, pos)
  356.        
  357.         if isIn(control, c) or isIn(whitespace, c) then
  358.             endPos = pos
  359.             break
  360.         end
  361.     end
  362.    
  363.     if not endPos then error("Error: Unexpected end of input near" .. start, 0) end
  364.     prevC = str:sub(start, endPos - 1)
  365.    
  366.     if prevC == "null" then
  367.         return nil, endPos
  368.     elseif prevC == "true" then
  369.         return true, endPos
  370.     elseif prevC== "false" then
  371.         return false, endPos
  372.     elseif tonumber(prevC) then
  373.         return tonumber(prevC), endPos
  374.     end
  375.    
  376.     error("Error: Invalid value near" .. start, 0)
  377. end
  378.  
  379. -----------------------------------------------------------------------------
  380. function JSON.decode(str)
  381.     local result
  382.     local c, start = JSON.nextValue(str, 1)
  383.    
  384.     if c == "{" then
  385.         result = JSON.parseObject(str, start)
  386.     elseif c == "[" then
  387.         result = JSON.parseArray(str, start)
  388.     else
  389.         error("Error: No Object or Array", 2)
  390.     end
  391.    
  392.     return result
  393. end
  394.  
  395. function JSON.download(url, name)
  396.     local data = utils.download(url, name)
  397.  
  398.     return JSON.decode(data)
  399. end
  400.    
  401. function git.getTreeName()
  402.     if args[1] == "latest" then
  403.         gitProfile.tree = "master"
  404.     elseif args[1] == "stable" then
  405.         gitProfile.tree = "stable"
  406.     else
  407.         gitProfile.tree = git.getLatestRelease()
  408.     end
  409. end
  410.  
  411. function git.getTree()
  412.     local tree =  JSON.download(gitProfile.api .. "/" .. gitProfile.username .. "/" .. gitProfile.reponame .. "/git/trees/" .. gitProfile.tree .. "?recursive=1&access_token=" .. gitProfile.access_token, "Tree " .. gitProfile.tree).tree
  413.     filesToDownload = #tree
  414.     return tree
  415. end
  416.  
  417. function git.getLatestRelease()
  418.     local release = JSON.download(gitProfile.api .. "/" .. gitProfile.username .. "/" .. gitProfile.reponame .. "/releases?access_token=" .. gitProfile.access_token, "Releases")[1]
  419.     return release.tag_name
  420. end
  421.  
  422. function git.downloadFiles()
  423.     local tree = git.getTree()
  424.     for _,object in pairs(tree) do
  425.        
  426.         if not isBlacklisted(object.path) then
  427.             if object.type == "blob" then
  428.                 git.downloadBlob(object.path)
  429.             else
  430.                 fs.makeDir(object.path)
  431.             end
  432.         end
  433.  
  434.         filesDownloaded = filesDownloaded + 1
  435.     end
  436. end
  437.  
  438. function git.downloadBlob(path)
  439.     local url = gitProfile.raw .. "/" .. gitProfile.username .. "/" .. gitProfile.reponame .. "/" .. gitProfile.tree .. "/"
  440.     local urlPath = ""
  441.  
  442.     for match in path:gmatch("[^/]+") do
  443.         urlPath = urlPath .. textutils.urlEncode(match) .. "/"
  444.     end
  445.  
  446.     urlPath = urlPath:sub(1, -2):gsub("%+", "%%20")
  447.  
  448.     local data = utils.download(url .. urlPath, path)  
  449.     local file = fs.open(path, "w")
  450.    
  451.     if not file then
  452.         error("Error: Failed to create file" .. path)
  453.     end
  454.    
  455.     file.write(data)
  456.     file.close()
  457. end
  458.  
  459. function utils.indexOf(tbl, value)
  460.     for k, v in pairs(tbl) do
  461.         if v == value then return k end
  462.     end
  463. end
  464.  
  465. function utils.download(url, name)
  466.     local tries = 0
  467.    
  468.     --drawBackground()
  469.     drawText("Downloading: " .. (name or url))
  470.     drawBar(filesDownloaded/filesToDownload)
  471.    
  472.     while tries < 5 do
  473.         http.request(url)
  474.        
  475.         local event, _url, data = coroutine.yield()
  476.        
  477.         if event == "http_success" and url == _url then
  478.             return data.readAll()
  479.         elseif event == "http_failure" then
  480.             tries = tries + 1
  481.         end
  482.     end
  483.        
  484.     drawText("Error: Download failed " .. url)
  485.     term.setCursorPos(1, ySize)
  486.     error()
  487. end
  488.  
  489. if not term.blit then
  490.     error("ComputerCraft version it too old", 0)
  491. end
  492.  
  493. if not http then
  494.     error("HTTP API is required to download LunaOS.", 0)
  495. end
  496.  
  497. if not term.native().isColour() then
  498.     error("An advanced computer is required to install LunaOS.", 0)
  499. end
  500.  
  501. term.setBackgroundColour(colours.grey)
  502. term.clear()
  503. image = decodeImage(image)
  504. drawImage()
  505. git.getTreeName()
  506. git.downloadFiles()
  507. term.setCursorPos(1, ySize)
  508. os.reboot()
Advertisement
Add Comment
Please, Sign In to add comment