SHOW:
|
|
- or go back to the newest paste.
| 1 | tArgs = {...}
| |
| 2 | ||
| 3 | if not term.isColor or not term.isColor() then | |
| 4 | error('OneOS Requires an Advanced (gold) Computer')
| |
| 5 | end | |
| 6 | ||
| 7 | _jstr = [[ | |
| 8 | local base = _G | |
| 9 | ||
| 10 | ----------------------------------------------------------------------------- | |
| 11 | -- Module declaration | |
| 12 | ----------------------------------------------------------------------------- | |
| 13 | ||
| 14 | -- Public functions | |
| 15 | ||
| 16 | -- Private functions | |
| 17 | local decode_scanArray | |
| 18 | local decode_scanComment | |
| 19 | local decode_scanConstant | |
| 20 | local decode_scanNumber | |
| 21 | local decode_scanObject | |
| 22 | local decode_scanString | |
| 23 | local decode_scanWhitespace | |
| 24 | local encodeString | |
| 25 | local isArray | |
| 26 | local isEncodable | |
| 27 | ||
| 28 | ----------------------------------------------------------------------------- | |
| 29 | -- PUBLIC FUNCTIONS | |
| 30 | ----------------------------------------------------------------------------- | |
| 31 | --- Encodes an arbitrary Lua object / variable. | |
| 32 | -- @param v The Lua object / variable to be JSON encoded. | |
| 33 | -- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) | |
| 34 | function encode (v) | |
| 35 | -- Handle nil values | |
| 36 | if v==nil then | |
| 37 | return "null" | |
| 38 | end | |
| 39 | ||
| 40 | local vtype = base.type(v) | |
| 41 | ||
| 42 | -- Handle strings | |
| 43 | if vtype=='string' then | |
| 44 | return '"' .. encodeString(v) .. '"' -- Need to handle encoding in string | |
| 45 | end | |
| 46 | ||
| 47 | -- Handle booleans | |
| 48 | if vtype=='number' or vtype=='boolean' then | |
| 49 | return base.tostring(v) | |
| 50 | end | |
| 51 | ||
| 52 | -- Handle tables | |
| 53 | if vtype=='table' then | |
| 54 | local rval = {}
| |
| 55 | -- Consider arrays separately | |
| 56 | local bArray, maxCount = isArray(v) | |
| 57 | if bArray then | |
| 58 | for i = 1,maxCount do | |
| 59 | table.insert(rval, encode(v[i])) | |
| 60 | end | |
| 61 | else -- An object, not an array | |
| 62 | for i,j in base.pairs(v) do | |
| 63 | if isEncodable(i) and isEncodable(j) then | |
| 64 | table.insert(rval, '"' .. encodeString(i) .. '":' .. encode(j)) | |
| 65 | end | |
| 66 | end | |
| 67 | end | |
| 68 | if bArray then | |
| 69 | return '[' .. table.concat(rval,',') ..']' | |
| 70 | else | |
| 71 | return '{' .. table.concat(rval,',') .. '}'
| |
| 72 | end | |
| 73 | end | |
| 74 | ||
| 75 | -- Handle null values | |
| 76 | if vtype=='function' and v==null then | |
| 77 | return 'null' | |
| 78 | end | |
| 79 | ||
| 80 | base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v)) | |
| 81 | end | |
| 82 | ||
| 83 | ||
| 84 | --- Decodes a JSON string and returns the decoded value as a Lua data structure / value. | |
| 85 | -- @param s The string to scan. | |
| 86 | -- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. | |
| 87 | -- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, | |
| 88 | -- and the position of the first character after | |
| 89 | -- the scanned JSON object. | |
| 90 | function decode(s, startPos) | |
| 91 | startPos = startPos and startPos or 1 | |
| 92 | startPos = decode_scanWhitespace(s,startPos) | |
| 93 | base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']') | |
| 94 | local curChar = string.sub(s,startPos,startPos) | |
| 95 | -- Object | |
| 96 | if curChar=='{' then
| |
| 97 | return decode_scanObject(s,startPos) | |
| 98 | end | |
| 99 | -- Array | |
| 100 | if curChar=='[' then | |
| 101 | return decode_scanArray(s,startPos) | |
| 102 | end | |
| 103 | -- Number | |
| 104 | if string.find("+-0123456789.e", curChar, 1, true) then
| |
| 105 | return decode_scanNumber(s,startPos) | |
| 106 | end | |
| 107 | -- String | |
| 108 | if curChar=='"' or curChar=="'" then | |
| 109 | return decode_scanString(s,startPos) | |
| 110 | end | |
| 111 | if string.sub(s,startPos,startPos+1)=='/*' then | |
| 112 | return decode(s, decode_scanComment(s,startPos)) | |
| 113 | end | |
| 114 | -- Otherwise, it must be a constant | |
| 115 | return decode_scanConstant(s,startPos) | |
| 116 | end | |
| 117 | ||
| 118 | --- The null function allows one to specify a null value in an associative array (which is otherwise | |
| 119 | -- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null }
| |
| 120 | function null() | |
| 121 | return null -- so json.null() will also return null ;-) | |
| 122 | end | |
| 123 | ----------------------------------------------------------------------------- | |
| 124 | -- Internal, PRIVATE functions. | |
| 125 | -- Following a Python-like convention, I have prefixed all these 'PRIVATE' | |
| 126 | -- functions with an underscore. | |
| 127 | ----------------------------------------------------------------------------- | |
| 128 | ||
| 129 | --- Scans an array from JSON into a Lua object | |
| 130 | -- startPos begins at the start of the array. | |
| 131 | -- Returns the array and the next starting position | |
| 132 | -- @param s The string being scanned. | |
| 133 | -- @param startPos The starting position for the scan. | |
| 134 | -- @return table, int The scanned array as a table, and the position of the next character to scan. | |
| 135 | function decode_scanArray(s,startPos) | |
| 136 | local array = {} -- The return value
| |
| 137 | local stringLen = string.len(s) | |
| 138 | base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s ) | |
| 139 | startPos = startPos + 1 | |
| 140 | -- Infinite loop for array elements | |
| 141 | repeat | |
| 142 | startPos = decode_scanWhitespace(s,startPos) | |
| 143 | base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.') | |
| 144 | local curChar = string.sub(s,startPos,startPos) | |
| 145 | if (curChar==']') then | |
| 146 | return array, startPos+1 | |
| 147 | end | |
| 148 | if (curChar==',') then | |
| 149 | startPos = decode_scanWhitespace(s,startPos+1) | |
| 150 | end | |
| 151 | base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.') | |
| 152 | object, startPos = decode(s,startPos) | |
| 153 | table.insert(array,object) | |
| 154 | until false | |
| 155 | end | |
| 156 | ||
| 157 | --- Scans a comment and discards the comment. | |
| 158 | -- Returns the position of the next character following the comment. | |
| 159 | -- @param string s The JSON string to scan. | |
| 160 | -- @param int startPos The starting position of the comment | |
| 161 | function decode_scanComment(s, startPos) | |
| 162 | base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos) | |
| 163 | local endPos = string.find(s,'*/',startPos+2) | |
| 164 | base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos) | |
| 165 | return endPos+2 | |
| 166 | end | |
| 167 | ||
| 168 | --- Scans for given constants: true, false or null | |
| 169 | -- Returns the appropriate Lua type, and the position of the next character to read. | |
| 170 | -- @param s The string being scanned. | |
| 171 | -- @param startPos The position in the string at which to start scanning. | |
| 172 | -- @return object, int The object (true, false or nil) and the position at which the next character should be | |
| 173 | -- scanned. | |
| 174 | function decode_scanConstant(s, startPos) | |
| 175 | local consts = { ["true"] = true, ["false"] = false, ["null"] = nil }
| |
| 176 | local constNames = {"true","false","null"}
| |
| 177 | ||
| 178 | for i,k in base.pairs(constNames) do | |
| 179 | --print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k)
| |
| 180 | if string.sub(s,startPos, startPos + string.len(k) -1 )==k then | |
| 181 | return consts[k], startPos + string.len(k) | |
| 182 | end | |
| 183 | end | |
| 184 | base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos) | |
| 185 | end | |
| 186 | ||
| 187 | --- Scans a number from the JSON encoded string. | |
| 188 | -- (in fact, also is able to scan numeric +- eqns, which is not | |
| 189 | -- in the JSON spec.) | |
| 190 | -- Returns the number, and the position of the next character | |
| 191 | -- after the number. | |
| 192 | -- @param s The string being scanned. | |
| 193 | -- @param startPos The position at which to start scanning. | |
| 194 | -- @return number, int The extracted number and the position of the next character to scan. | |
| 195 | function decode_scanNumber(s,startPos) | |
| 196 | local endPos = startPos+1 | |
| 197 | local stringLen = string.len(s) | |
| 198 | local acceptableChars = "+-0123456789.e" | |
| 199 | while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) | |
| 200 | and endPos<=stringLen | |
| 201 | ) do | |
| 202 | endPos = endPos + 1 | |
| 203 | end | |
| 204 | local stringValue = 'return ' .. string.sub(s,startPos, endPos-1) | |
| 205 | local stringEval = base.loadstring(stringValue) | |
| 206 | base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos) | |
| 207 | return stringEval(), endPos | |
| 208 | end | |
| 209 | ||
| 210 | --- Scans a JSON object into a Lua object. | |
| 211 | -- startPos begins at the start of the object. | |
| 212 | -- Returns the object and the next starting position. | |
| 213 | -- @param s The string being scanned. | |
| 214 | -- @param startPos The starting position of the scan. | |
| 215 | -- @return table, int The scanned object as a table and the position of the next character to scan. | |
| 216 | function decode_scanObject(s,startPos) | |
| 217 | local object = {}
| |
| 218 | local stringLen = string.len(s) | |
| 219 | local key, value | |
| 220 | base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s)
| |
| 221 | startPos = startPos + 1 | |
| 222 | repeat | |
| 223 | startPos = decode_scanWhitespace(s,startPos) | |
| 224 | base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.') | |
| 225 | local curChar = string.sub(s,startPos,startPos) | |
| 226 | if (curChar=='}') then | |
| 227 | return object,startPos+1 | |
| 228 | end | |
| 229 | if (curChar==',') then | |
| 230 | startPos = decode_scanWhitespace(s,startPos+1) | |
| 231 | end | |
| 232 | base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.') | |
| 233 | -- Scan the key | |
| 234 | key, startPos = decode(s,startPos) | |
| 235 | base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) | |
| 236 | startPos = decode_scanWhitespace(s,startPos) | |
| 237 | base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) | |
| 238 | base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos) | |
| 239 | startPos = decode_scanWhitespace(s,startPos+1) | |
| 240 | base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) | |
| 241 | value, startPos = decode(s,startPos) | |
| 242 | object[key]=value | |
| 243 | until false -- infinite loop while key-value pairs are found | |
| 244 | end | |
| 245 | ||
| 246 | --- Scans a JSON string from the opening inverted comma or single quote to the | |
| 247 | -- end of the string. | |
| 248 | -- Returns the string extracted as a Lua string, | |
| 249 | -- and the position of the next non-string character | |
| 250 | -- (after the closing inverted comma or single quote). | |
| 251 | -- @param s The string being scanned. | |
| 252 | -- @param startPos The starting position of the scan. | |
| 253 | -- @return string, int The extracted string as a Lua string, and the next character to parse. | |
| 254 | function decode_scanString(s,startPos) | |
| 255 | base.assert(startPos, 'decode_scanString(..) called without start position') | |
| 256 | local startChar = string.sub(s,startPos,startPos) | |
| 257 | base.assert(startChar=="'" or startChar=='"','decode_scanString called for a non-string') | |
| 258 | local escaped = false | |
| 259 | local endPos = startPos + 1 | |
| 260 | local bEnded = false | |
| 261 | local stringLen = string.len(s) | |
| 262 | repeat | |
| 263 | local curChar = string.sub(s,endPos,endPos) | |
| 264 | -- Character escaping is only used to escape the string delimiters | |
| 265 | if not escaped then | |
| 266 | if curChar=='\\' then | |
| 267 | escaped = true | |
| 268 | else | |
| 269 | bEnded = curChar==startChar | |
| 270 | end | |
| 271 | else | |
| 272 | -- If we're escaped, we accept the current character come what may | |
| 273 | escaped = false | |
| 274 | end | |
| 275 | endPos = endPos + 1 | |
| 276 | base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos) | |
| 277 | until bEnded | |
| 278 | local stringValue = 'return ' .. string.sub(s, startPos, endPos-1) | |
| 279 | local stringEval = base.loadstring(stringValue) | |
| 280 | base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos) | |
| 281 | return stringEval(), endPos | |
| 282 | end | |
| 283 | ||
| 284 | --- Scans a JSON string skipping all whitespace from the current start position. | |
| 285 | -- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. | |
| 286 | -- @param s The string being scanned | |
| 287 | -- @param startPos The starting position where we should begin removing whitespace. | |
| 288 | -- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string | |
| 289 | -- was reached. | |
| 290 | function decode_scanWhitespace(s,startPos) | |
| 291 | local whitespace=" \n\r\t" | |
| 292 | local stringLen = string.len(s) | |
| 293 | while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do | |
| 294 | startPos = startPos + 1 | |
| 295 | end | |
| 296 | return startPos | |
| 297 | end | |
| 298 | ||
| 299 | --- Encodes a string to be JSON-compatible. | |
| 300 | -- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) | |
| 301 | -- @param s The string to return as a JSON encoded (i.e. backquoted string) | |
| 302 | -- @return The string appropriately escaped. | |
| 303 | function encodeString(s) | |
| 304 | s = string.gsub(s,'\\','\\\\') | |
| 305 | s = string.gsub(s,'"','\\"') | |
| 306 | s = string.gsub(s,"'","\\'") | |
| 307 | s = string.gsub(s,'\n','\\n') | |
| 308 | s = string.gsub(s,'\t','\\t') | |
| 309 | return s | |
| 310 | end | |
| 311 | ||
| 312 | -- Determines whether the given Lua type is an array or a table / dictionary. | |
| 313 | -- We consider any table an array if it has indexes 1..n for its n items, and no | |
| 314 | -- other data in the table. | |
| 315 | -- I think this method is currently a little 'flaky', but can't think of a good way around it yet... | |
| 316 | -- @param t The table to evaluate as an array | |
| 317 | -- @return boolean, number True if the table can be represented as an array, false otherwise. If true, | |
| 318 | -- the second returned value is the maximum | |
| 319 | -- number of indexed elements in the array. | |
| 320 | function isArray(t) | |
| 321 | -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable | |
| 322 | -- (with the possible exception of 'n') | |
| 323 | local maxIndex = 0 | |
| 324 | for k,v in base.pairs(t) do | |
| 325 | if (base.type(k)=='number' and math.floor(k)==k and 1<=k) then -- k,v is an indexed pair | |
| 326 | if (not isEncodable(v)) then return false end -- All array elements must be encodable | |
| 327 | maxIndex = math.max(maxIndex,k) | |
| 328 | else | |
| 329 | if (k=='n') then | |
| 330 | if v ~= table.getn(t) then return false end -- False if n does not hold the number of elements | |
| 331 | else -- Else of (k=='n') | |
| 332 | if isEncodable(v) then return false end | |
| 333 | end -- End of (k~='n') | |
| 334 | end -- End of k,v not an indexed pair | |
| 335 | end -- End of loop across all pairs | |
| 336 | return true, maxIndex | |
| 337 | end | |
| 338 | ||
| 339 | --- Determines whether the given Lua object / table / variable can be JSON encoded. The only | |
| 340 | -- types that are JSON encodable are: string, boolean, number, nil, table and json.null. | |
| 341 | -- In this implementation, all other types are ignored. | |
| 342 | -- @param o The object to examine. | |
| 343 | -- @return boolean True if the object should be JSON encoded, false if it should be ignored. | |
| 344 | function isEncodable(o) | |
| 345 | local t = base.type(o) | |
| 346 | return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null) | |
| 347 | end | |
| 348 | ]] | |
| 349 | ||
| 350 | function loadJSON() | |
| 351 | local sName = 'JSON' | |
| 352 | ||
| 353 | local tEnv = {}
| |
| 354 | setmetatable( tEnv, { __index = _G } )
| |
| 355 | local fnAPI, err = loadstring(_jstr) | |
| 356 | if fnAPI then | |
| 357 | setfenv( fnAPI, tEnv ) | |
| 358 | fnAPI() | |
| 359 | else | |
| 360 | printError( err ) | |
| 361 | return false | |
| 362 | end | |
| 363 | ||
| 364 | local tAPI = {}
| |
| 365 | for k,v in pairs( tEnv ) do | |
| 366 | tAPI[k] = v | |
| 367 | end | |
| 368 | ||
| 369 | _G[sName] = tAPI | |
| 370 | return true | |
| 371 | end | |
| 372 | ||
| 373 | local mainTitle = 'OneOS Installer' | |
| 374 | local subTitle = 'Please wait...' | |
| 375 | ||
| 376 | function Draw() | |
| 377 | sleep(0) | |
| 378 | term.setBackgroundColour(colours.white) | |
| 379 | term.clear() | |
| 380 | local w, h = term.getSize() | |
| 381 | term.setTextColour(colours.lightBlue) | |
| 382 | term.setCursorPos(math.ceil((w-#mainTitle)/2), 8) | |
| 383 | term.write(mainTitle) | |
| 384 | term.setTextColour(colours.blue) | |
| 385 | term.setCursorPos(math.ceil((w-#subTitle)/2), 10) | |
| 386 | term.write(subTitle) | |
| 387 | end | |
| 388 | ||
| 389 | tArgs = {...}
| |
| 390 | ||
| 391 | Settings = {
| |
| 392 | InstallPath = '/', --Where the program's installed, don't always asume root (if it's run under something like OneOS) | |
| 393 | Hidden = false, --Whether or not the update is hidden (doesn't write to the screen), useful for background updates | |
| 394 | GitHubUsername = 'oeed', --Your GitHub username as it appears in the URL | |
| 395 | GitHubRepoName = 'OneOS', --The repo name as it appears in the URL | |
| 396 | DownloadReleases = true, --If true it will download the latest release, otherwise it will download the files as they currently appear | |
| 397 | UpdateFunction = nil, --Sent when something happens (file downloaded etc.) | |
| 398 | TotalBytes = 0, --Do not change this value (especially programatically)! | |
| 399 | DownloadedBytes = 0, --Do not change this value (especially programatically)! | |
| 400 | Status = '', | |
| 401 | SecondaryStatus = '', | |
| 402 | } | |
| 403 | ||
| 404 | loadJSON() | |
| 405 | ||
| 406 | function downloadJSON(path) | |
| 407 | local _json = http.get(path) | |
| 408 | if not _json then | |
| 409 | error('Could not download: '..path..' Check your connection.')
| |
| 410 | end | |
| 411 | return JSON.decode(_json.readAll()) | |
| 412 | end | |
| 413 | ||
| 414 | if http then | |
| 415 | subTitle = 'HTTP enabled, attempting update...' | |
| 416 | Draw() | |
| 417 | else | |
| 418 | subTitle = 'HTTP is required to update.' | |
| 419 | Draw() | |
| 420 | error('')
| |
| 421 | end | |
| 422 | ||
| 423 | subTitle = 'Determining Latest Version' | |
| 424 | Draw() | |
| 425 | local releases = downloadJSON('https://api.github.com/repos/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/releases')
| |
| 426 | local latestReleaseTag = releases[1].tag_name | |
| 427 | if not tArgs or #tArgs ~= 1 and tArgs[1] ~= 'beta' then | |
| 428 | for i, v in ipairs(releases) do | |
| 429 | if not v.prerelease then | |
| 430 | latestReleaseTag = v.tag_name | |
| 431 | break | |
| 432 | end | |
| 433 | end | |
| 434 | end | |
| 435 | subTitle = 'Optaining Latest Version URL' | |
| 436 | Draw() | |
| 437 | local refs = downloadJSON('https://api.github.com/repos/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/git/refs/tags/'..latestReleaseTag)
| |
| 438 | local latestReleaseSha = refs.object.sha | |
| 439 | ||
| 440 | subTitle = 'Downloading File Listing' | |
| 441 | Draw() | |
| 442 | ||
| 443 | local tree = downloadJSON('https://api.github.com/repos/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/git/trees/'..latestReleaseSha..'?recursive=1').tree
| |
| 444 | ||
| 445 | local blacklist = {
| |
| 446 | '/.gitignore', | |
| 447 | '/README.md', | |
| 448 | '/TODO', | |
| 449 | '/Desktop/.Desktop.settings', | |
| 450 | '/.version' | |
| 451 | } | |
| 452 | ||
| 453 | function isBlacklisted(path) | |
| 454 | for i, item in ipairs(blacklist) do | |
| 455 | if item == path then | |
| 456 | return true | |
| 457 | end | |
| 458 | end | |
| 459 | return false | |
| 460 | end | |
| 461 | ||
| 462 | Settings.TotalFiles = 0 | |
| 463 | Settings.TotalBytes = 0 | |
| 464 | for i, v in ipairs(tree) do | |
| 465 | if not isBlacklisted(Settings.InstallPath..v.path) and v.size then | |
| 466 | Settings.TotalBytes = Settings.TotalBytes + v.size | |
| 467 | Settings.TotalFiles = Settings.TotalFiles + 1 | |
| 468 | end | |
| 469 | end | |
| 470 | ||
| 471 | Settings.DownloadedBytes = 0 | |
| 472 | Settings.DownloadedFiles = 0 | |
| 473 | function downloadBlob(v) | |
| 474 | if isBlacklisted(Settings.InstallPath..v.path) then | |
| 475 | return | |
| 476 | end | |
| 477 | if v.type == 'tree' then | |
| 478 | -- subTitle = 'Making folder: '..'/'..Settings.InstallPath..v.path | |
| 479 | Draw() | |
| 480 | fs.makeDir('/'..Settings.InstallPath..v.path)
| |
| 481 | else | |
| 482 | -- subTitle = 'Starting download for: '..Settings.InstallPath..v.path | |
| 483 | Draw() | |
| 484 | ||
| 485 | local tries, f = 0 | |
| 486 | - | repeat |
| 486 | + | repeat |
| 487 | - | f = http.get(('https://raw.github.com/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/'..latestReleaseTag..Settings.InstallPath..v.path):gsub(' ','%%20'))
|
| 487 | + | local url = 'https://raw.github.com/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/'..latestReleaseTag..Settings.InstallPath..v.path |
| 488 | f = http.get(url:gsub(' ','%%20'), nil)
| |
| 489 | if not f then sleep(5) end | |
| 490 | tries = tries + 1 | |
| 491 | until tries > 5 or f | |
| 492 | ||
| 493 | if not f then | |
| 494 | error('Downloading failed, try again. '..('https://raw.github.com/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/'..latestReleaseTag..Settings.InstallPath..v.path):gsub(' ','%%20'))
| |
| 495 | end | |
| 496 | ||
| 497 | local h = fs.open('/'..Settings.InstallPath..v.path, 'w')
| |
| 498 | h.write(f.readAll()) | |
| 499 | h.close() | |
| 500 | -- subTitle = 'Downloading: ' .. math.floor(100*(Settings.DownloadedBytes/Settings.TotalBytes))..'%' | |
| 501 | subTitle = 'Downloading: ' .. math.floor(100*(Settings.DownloadedFiles/Settings.TotalFiles))..'%' -- using the number of files over the number of bytes actually appears to be more accurate, the connection takes longer than sending the data | |
| 502 | -- subTitle = '('..math.floor(100*(Settings.DownloadedBytes/Settings.TotalBytes))..'%) Downloaded: '..Settings.InstallPath..v.path
| |
| 503 | Draw() | |
| 504 | if v.size then | |
| 505 | Settings.DownloadedBytes = Settings.DownloadedBytes + v.size | |
| 506 | Settings.DownloadedFiles = Settings.DownloadedFiles + 1 | |
| 507 | end | |
| 508 | end | |
| 509 | end | |
| 510 | ||
| 511 | local connectionLimit = 5 | |
| 512 | local downloads = {}
| |
| 513 | for i, v in ipairs(tree) do | |
| 514 | local queueNumber = math.ceil(i / connectionLimit) | |
| 515 | if not downloads[queueNumber] then | |
| 516 | downloads[queueNumber] = {}
| |
| 517 | end | |
| 518 | table.insert(downloads[queueNumber], function() | |
| 519 | downloadBlob(v) | |
| 520 | end) | |
| 521 | end | |
| 522 | ||
| 523 | for i, queue in ipairs(downloads) do | |
| 524 | parallel.waitForAll(unpack(queue)) | |
| 525 | end | |
| 526 | ||
| 527 | local h = fs.open('/System/.version', 'w')
| |
| 528 | h.write(latestReleaseTag) | |
| 529 | h.close() | |
| 530 | ||
| 531 | mainTitle = 'Installation Complete!' | |
| 532 | subTitle = 'Rebooting in 1 second...' | |
| 533 | Draw() | |
| 534 | sleep(1) | |
| 535 | os.reboot() |