Advertisement
ElvishJerricco

Grin

Sep 30th, 2014
1,617
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 47.18 KB | None | 0 0
  1. -- use build.xml to import the zip and json libraries directly
  2.  
  3. local zip = setmetatable({}, {__index=getfenv()})
  4. local json = setmetatable({}, {__index=getfenv()})
  5. local base64 = setmetatable({}, {__index=getfenv()})
  6. local argparse = setmetatable({}, {__index=getfenv()})
  7.  
  8. do
  9.     local function zip_api_make()
  10.         --[[
  11.  
  12. LUA MODULE
  13.  
  14.     compress.deflatelua - deflate (and gunzip/zlib) implemented in Lua.
  15.  
  16. SYNOPSIS
  17.    
  18.     local DEFLATE = require 'compress.deflatelua'
  19.     -- uncompress gzip file
  20.     local fh = assert(io.open'foo.txt.gz', 'rb')
  21.     local ofh = assert(io.open'foo.txt', 'wb')
  22.     DEFLATE.gunzip {input=fh, output=ofh}
  23.     fh:close(); ofh:close()
  24.     -- can also uncompress from string including zlib and raw DEFLATE formats.
  25.    
  26. DESCRIPTION
  27.    
  28.     This is a pure Lua implementation of decompressing the DEFLATE format,
  29.     including the related zlib and gzip formats.
  30.    
  31.     Note: This library only supports decompression.
  32.     Compression is not currently implemented.
  33.  
  34. API
  35.  
  36.     Note: in the following functions, input stream `fh` may be
  37.     a file handle, string, or an iterator function that returns strings.
  38.     Output stream `ofh` may be a file handle or a function that
  39.     consumes one byte (number 0..255) per call.
  40.  
  41.     DEFLATE.inflate {input=fh, output=ofh}
  42.  
  43.         Decompresses input stream `fh` in the DEFLATE format
  44.         while writing to output stream `ofh`.
  45.         DEFLATE is detailed in http://tools.ietf.org/html/rfc1951 .
  46.    
  47.     DEFLATE.gunzip {input=fh, output=ofh, disable_crc=disable_crc}
  48.    
  49.         Decompresses input stream `fh` with the gzip format
  50.         while writing to output stream `ofh`.
  51.         `disable_crc` (defaults to `false`) will disable CRC-32 checking
  52.         to increase speed.
  53.         gzip is detailed in http://tools.ietf.org/html/rfc1952 .
  54.  
  55.     DEFLATE.inflate_zlib {input=fh, output=ofh, disable_crc=disable_crc}
  56.    
  57.         Decompresses input stream `fh` with the zlib format
  58.         while writing to output stream `ofh`.
  59.         `disable_crc` (defaults to `false`) will disable CRC-32 checking
  60.         to increase speed.
  61.         zlib is detailed in http://tools.ietf.org/html/rfc1950 .  
  62.  
  63.     DEFLATE.adler32(byte, crc) --> rcrc
  64.    
  65.         Returns adler32 checksum of byte `byte` (number 0..255) appended
  66.         to string with adler32 checksum `crc`.  This is internally used by
  67.         `inflate_zlib`.
  68.         ADLER32 in detailed in http://tools.ietf.org/html/rfc1950 .
  69.  
  70. COMMAND LINE UTILITY
  71.  
  72.     A `gunziplua` command line utility (in folder `bin`) is also provided.
  73.     This mimicks the *nix `gunzip` utility but is a pure Lua implementation
  74.     that invokes this library.  For help do
  75.    
  76.         gunziplua -h
  77.        
  78. DEPENDENCIES
  79.  
  80.     Requires 'digest.crc32lua' (used for optional CRC-32 checksum checks).
  81.         https://github.com/davidm/lua-digest-crc32lua
  82.  
  83.     Will use a bit library ('bit', 'bit32', 'bit.numberlua') if available.  This
  84.     is not that critical for this library but is required by digest.crc32lua.
  85.  
  86.     'pythonic.optparse' is only required by the optional `gunziplua`
  87.     command-line utilty for command line parsing.  
  88.         https://github.com/davidm/lua-pythonic-optparse
  89.  
  90. INSTALLATION
  91.  
  92.     Copy the `compress` directory into your LUA_PATH.
  93.        
  94. REFERENCES
  95.  
  96.     [1] DEFLATE Compressed Data Format Specification version 1.3
  97.             http://tools.ietf.org/html/rfc1951
  98.     [2] GZIP file format specification version 4.3
  99.             http://tools.ietf.org/html/rfc1952
  100.     [3] http://en.wikipedia.org/wiki/DEFLATE
  101.     [4] pyflate, by Paul Sladen
  102.             http://www.paul.sladen.org/projects/pyflate/
  103.     [5] Compress::Zlib::Perl - partial pure Perl implementation of
  104.             Compress::Zlib
  105.             http://search.cpan.org/~nwclark/Compress-Zlib-Perl/Perl.pm
  106.  
  107. LICENSE
  108.  
  109.     (c) 2008-2011 David Manura.  Licensed under the same terms as Lua (MIT).
  110.  
  111.     Permission is hereby granted, free of charge, to any person obtaining a copy
  112.     of this software and associated documentation files (the "Software"), to deal
  113.     in the Software without restriction, including without limitation the rights
  114.     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  115.     copies of the Software, and to permit persons to whom the Software is
  116.     furnished to do so, subject to the following conditions:
  117.  
  118.     The above copyright notice and this permission notice shall be included in
  119.     all copies or substantial portions of the Software.
  120.  
  121.     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  122.     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  123.     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  124.     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  125.     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  126.     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  127.     THE SOFTWARE.
  128.     (end license)
  129. --]]
  130.  
  131.  
  132. local assert = assert
  133. local error = error
  134. local ipairs = ipairs
  135. local pairs = pairs
  136. local print = print
  137. local require = require
  138. local tostring = tostring
  139. local type = type
  140. local setmetatable = setmetatable
  141. local io = io
  142. local math = math
  143. local table_sort = table.sort
  144. local math_max = math.max
  145. local string_char = string.char
  146.  
  147. --[[
  148.  Requires the first module listed that exists, else raises like `require`.
  149.  If a non-string is encountered, it is returned.
  150.  Second return value is module name loaded (or '').
  151.  --]]
  152. local function requireany(...)
  153.     local errs = {}
  154.     for i = 1, select('#', ...) do local name = select(i, ...)
  155.         if type(name) ~= 'string' then return name, '' end
  156.         local ok, mod = pcall(require, name)
  157.         if ok then return mod, name end
  158.         errs[#errs+1] = mod
  159.     end
  160.     error(table.concat(errs, '\n'), 2)
  161. end
  162.  
  163.  
  164. --local crc32 = require "digest.crc32lua" . crc32_byte
  165. --local bit, name_ = requireany('bit', 'bit32', 'bit.numberlua', nil)
  166. local bit = bit
  167.  
  168. local DEBUG = false
  169.  
  170. -- Whether to use `bit` library functions in current module.
  171. -- Unlike the crc32 library, it doesn't make much difference in this module.
  172. local NATIVE_BITOPS = false--(bit ~= nil) -- not sure why, but this code didn't work with the bit library
  173.  
  174. local band, lshift, rshift
  175. if NATIVE_BITOPS then
  176.     band = bit.band
  177.     lshift = bit.blshift
  178.     rshift = bit.brshift
  179. end
  180.  
  181.  
  182. local function warn(s)
  183.     io.stderr:write(s, '\n')
  184. end
  185.  
  186.  
  187. local function debug(...)
  188.     print('DEBUG', ...)
  189. end
  190.  
  191.  
  192. local function runtime_error(s, level)
  193.     level = level or 1
  194.     error(s, level+1)
  195. end
  196.  
  197.  
  198. local function make_outstate(outbs)
  199.     local outstate = {}
  200.     outstate.outbs = outbs
  201.     outstate.window = {}
  202.     outstate.window_pos = 1
  203.     return outstate
  204. end
  205.  
  206.  
  207. local function output(outstate, byte)
  208.     -- debug('OUTPUT:', s)
  209.     local window_pos = outstate.window_pos
  210.     outstate.outbs(byte)
  211.     outstate.window[window_pos] = byte
  212.     outstate.window_pos = window_pos % 32768 + 1  -- 32K
  213. end
  214.  
  215.  
  216. local function noeof(val)
  217.     return assert(val, 'unexpected end of file')
  218. end
  219.  
  220.  
  221. local function hasbit(bits, bit)
  222.     return bits % (bit + bit) >= bit
  223. end
  224.  
  225.  
  226. local function memoize(f)
  227.     local mt = {}
  228.     local t = setmetatable({}, mt)
  229.     function mt:__index(k)
  230.         local v = f(k)
  231.         t[k] = v
  232.         return v
  233.     end
  234.     return t
  235. end
  236.  
  237.  
  238. -- small optimization (lookup table for powers of 2)
  239. local pow2 = memoize(function(n) return 2^n end)
  240.  
  241. --local tbits = memoize(
  242. --  function(bits)
  243. --    return memoize( function(bit) return getbit(bits, bit) end )
  244. --  end )
  245.  
  246.  
  247. -- weak metatable marking objects as bitstream type
  248. local is_bitstream = setmetatable({}, {__mode='k'})
  249.  
  250.  
  251. -- DEBUG
  252. -- prints LSB first
  253. --[[
  254. local function bits_tostring(bits, nbits)
  255.     local s = ''
  256.     local tmp = bits
  257.     local function f()
  258.         local b = tmp % 2 == 1 and 1 or 0
  259.         s = s .. b
  260.         tmp = (tmp - b) / 2
  261.     end
  262.     if nbits then
  263.         for i=1,nbits do f() end
  264.     else
  265.         while tmp ~= 0 do f() end
  266.     end
  267.  
  268.     return s
  269. end
  270. --]]
  271.  
  272. local function bytestream_from_file(fh)
  273.     local o = {}
  274.     function o:read()
  275.         local sb = fh:read(1)
  276.         if sb then return sb:byte() end
  277.     end
  278.     return o
  279. end
  280.  
  281.  
  282. local function bytestream_from_string(s)
  283.     local i = 1
  284.     local o = {}
  285.     function o:read()
  286.         local by
  287.         if i <= #s then
  288.             by = s:byte(i)
  289.             i = i + 1
  290.         end
  291.         return by
  292.     end
  293.     return o
  294. end
  295.  
  296.  
  297. local function bytestream_from_function(f)
  298.     local i = 0
  299.     local buffer = ''
  300.     local o = {}
  301.     function o:read()
  302.         i = i + 1
  303.         if i > #buffer then
  304.             buffer = f()
  305.             if not buffer then return end
  306.             i = 1
  307.         end
  308.         return buffer:byte(i,i)
  309.     end
  310.     return o
  311. end
  312.  
  313.  
  314. local function bitstream_from_bytestream(bys)
  315.     local buf_byte = 0
  316.     local buf_nbit = 0
  317.     local o = {}
  318.  
  319.     function o:nbits_left_in_byte()
  320.         return buf_nbit
  321.     end
  322.  
  323.     if NATIVE_BITOPS then
  324.         function o:read(nbits)
  325.             nbits = nbits or 1
  326.             while buf_nbit < nbits do
  327.                 local byte = bys:read()
  328.                 if not byte then return end  -- note: more calls also return nil
  329.                 buf_byte = buf_byte + lshift(byte, buf_nbit)
  330.                 buf_nbit = buf_nbit + 8
  331.             end
  332.             local bits
  333.             if nbits == 0 then
  334.                 bits = 0
  335.             elseif nbits == 32 then
  336.                 bits = buf_byte
  337.                 buf_byte = 0
  338.             else
  339.                 bits = band(buf_byte, rshift(0xffffffff, 32 - nbits))
  340.                 buf_byte = rshift(buf_byte, nbits)
  341.             end
  342.             buf_nbit = buf_nbit - nbits
  343.             return bits
  344.         end
  345.     else
  346.         function o:read(nbits)
  347.             nbits = nbits or 1
  348.             while buf_nbit < nbits do
  349.                 local byte = bys:read()
  350.                 if not byte then return end  -- note: more calls also return nil
  351.                 buf_byte = buf_byte + pow2[buf_nbit] * byte
  352.                 buf_nbit = buf_nbit + 8
  353.             end
  354.             local m = pow2[nbits]
  355.             local bits = buf_byte % m
  356.             buf_byte = (buf_byte - bits) / m
  357.             buf_nbit = buf_nbit - nbits
  358.             return bits
  359.         end
  360.     end
  361.    
  362.     is_bitstream[o] = true
  363.  
  364.     return o
  365. end
  366.  
  367.  
  368. local function get_bitstream(o)
  369.     local bs
  370.     if is_bitstream[o] then
  371.         return o
  372.     elseif io.type(o) == 'file' then
  373.         bs = bitstream_from_bytestream(bytestream_from_file(o))
  374.     elseif type(o) == 'string' then
  375.         bs = bitstream_from_bytestream(bytestream_from_string(o))
  376.     elseif type(o) == 'function' then
  377.         bs = bitstream_from_bytestream(bytestream_from_function(o))
  378.     else
  379.         runtime_error 'unrecognized type'
  380.     end
  381.     return bs
  382. end
  383.  
  384.  
  385. local function get_obytestream(o)
  386.     local bs
  387.     if io.type(o) == 'file' then
  388.         bs = function(sbyte) o:write(string_char(sbyte)) end
  389.     elseif type(o) == 'function' then
  390.         bs = o
  391.     else
  392.         runtime_error('unrecognized type: ' .. tostring(o))
  393.     end
  394.     return bs
  395. end
  396.  
  397.  
  398. local function HuffmanTable(init, is_full)
  399.     local t = {}
  400.     if is_full then
  401.         for val,nbits in pairs(init) do
  402.             if nbits ~= 0 then
  403.                 t[#t+1] = {val=val, nbits=nbits}
  404.                 --debug('*',val,nbits)
  405.             end
  406.         end
  407.     else
  408.         for i=1,#init-2,2 do
  409.             local firstval, nbits, nextval = init[i], init[i+1], init[i+2]
  410.             --debug(val, nextval, nbits)
  411.             if nbits ~= 0 then
  412.                 for val=firstval,nextval-1 do
  413.                     t[#t+1] = {val=val, nbits=nbits}
  414.                 end
  415.             end
  416.         end
  417.     end
  418.     table_sort(t, function(a,b)
  419.         return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits
  420.     end)
  421.  
  422.     -- assign codes
  423.     local code = 1  -- leading 1 marker
  424.     local nbits = 0
  425.     for i,s in ipairs(t) do
  426.         if s.nbits ~= nbits then
  427.             code = code * pow2[s.nbits - nbits]
  428.             nbits = s.nbits
  429.         end
  430.         s.code = code
  431.         --debug('huffman code:', i, s.nbits, s.val, code, bits_tostring(code))
  432.         code = code + 1
  433.     end
  434.  
  435.     local minbits = math.huge
  436.     local look = {}
  437.     for i,s in ipairs(t) do
  438.         minbits = math.min(minbits, s.nbits)
  439.         look[s.code] = s.val
  440.     end
  441.  
  442.     --for _,o in ipairs(t) do
  443.     --  debug(':', o.nbits, o.val)
  444.     --end
  445.  
  446.     -- function t:lookup(bits) return look[bits] end
  447.  
  448.     local msb = NATIVE_BITOPS and function(bits, nbits)
  449.         local res = 0
  450.         for i=1,nbits do
  451.             res = lshift(res, 1) + band(bits, 1)
  452.             bits = rshift(bits, 1)
  453.         end
  454.         return res
  455.     end or function(bits, nbits)
  456.         local res = 0
  457.         for i=1,nbits do
  458.             local b = bits % 2
  459.             bits = (bits - b) / 2
  460.             res = res * 2 + b
  461.         end
  462.         return res
  463.     end
  464.    
  465.     local tfirstcode = memoize(
  466.         function(bits) return pow2[minbits] + msb(bits, minbits) end)
  467.  
  468.     function t:read(bs)
  469.         local code = 1 -- leading 1 marker
  470.         local nbits = 0
  471.         while 1 do
  472.             if nbits == 0 then  -- small optimization (optional)
  473.                 code = tfirstcode[noeof(bs:read(minbits))]
  474.                 nbits = nbits + minbits
  475.             else
  476.                 local b = noeof(bs:read())
  477.                 nbits = nbits + 1
  478.                 code = code * 2 + b   -- MSB first
  479.                 --[[NATIVE_BITOPS
  480.                 code = lshift(code, 1) + b   -- MSB first
  481.                 --]]
  482.             end
  483.             --debug('code?', code, bits_tostring(code))
  484.             local val = look[code]
  485.             if val then
  486.                 --debug('FOUND', val)
  487.                 return val
  488.             end
  489.         end
  490.     end
  491.  
  492.     return t
  493. end
  494.  
  495.  
  496. local function parse_gzip_header(bs)
  497.     -- local FLG_FTEXT = 2^0
  498.     local FLG_FHCRC = 2^1
  499.     local FLG_FEXTRA = 2^2
  500.     local FLG_FNAME = 2^3
  501.     local FLG_FCOMMENT = 2^4
  502.  
  503.     local id1 = bs:read(8)
  504.     local id2 = bs:read(8)
  505.     if id1 ~= 31 or id2 ~= 139 then
  506.         runtime_error 'not in gzip format'
  507.     end
  508.     local cm = bs:read(8)  -- compression method
  509.     local flg = bs:read(8) -- FLaGs
  510.     local mtime = bs:read(32) -- Modification TIME
  511.     local xfl = bs:read(8) -- eXtra FLags
  512.     local os = bs:read(8) -- Operating System
  513.  
  514.     if DEBUG then
  515.         debug("CM=", cm)
  516.         debug("FLG=", flg)
  517.         debug("MTIME=", mtime)
  518.         -- debug("MTIME_str=",os.date("%Y-%m-%d %H:%M:%S",mtime)) -- non-portable
  519.         debug("XFL=", xfl)
  520.         debug("OS=", os)
  521.     end
  522.  
  523.     if not os then runtime_error 'invalid header' end
  524.  
  525.     if hasbit(flg, FLG_FEXTRA) then
  526.         local xlen = bs:read(16)
  527.         local extra = 0
  528.         for i=1,xlen do
  529.             extra = bs:read(8)
  530.         end
  531.         if not extra then runtime_error 'invalid header' end
  532.     end
  533.  
  534.     local function parse_zstring(bs)
  535.         repeat
  536.             local by = bs:read(8)
  537.             if not by then runtime_error 'invalid header' end
  538.         until by == 0
  539.     end
  540.  
  541.     if hasbit(flg, FLG_FNAME) then
  542.         parse_zstring(bs)
  543.     end
  544.  
  545.     if hasbit(flg, FLG_FCOMMENT) then
  546.         parse_zstring(bs)
  547.     end
  548.  
  549.     if hasbit(flg, FLG_FHCRC) then
  550.         local crc16 = bs:read(16)
  551.         if not crc16 then runtime_error 'invalid header' end
  552.         -- IMPROVE: check CRC.  where is an example .gz file that
  553.         -- has this set?
  554.         if DEBUG then
  555.             debug("CRC16=", crc16)
  556.         end
  557.     end
  558. end
  559.  
  560. local function parse_zlib_header(bs)
  561.     local cm = bs:read(4) -- Compression Method
  562.     local cinfo = bs:read(4) -- Compression info
  563.     local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG)
  564.     local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary)
  565.     local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level)
  566.     local cmf = cinfo * 16  + cm -- CMF (Compresion Method and flags)
  567.     local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs
  568.    
  569.     if cm ~= 8 then -- not "deflate"
  570.         runtime_error("unrecognized zlib compression method: " + cm)
  571.     end
  572.     if cinfo > 7 then
  573.         runtime_error("invalid zlib window size: cinfo=" + cinfo)
  574.     end
  575.     local window_size = 2^(cinfo + 8)
  576.    
  577.     if (cmf*256 + flg) %  31 ~= 0 then
  578.         runtime_error("invalid zlib header (bad fcheck sum)")
  579.     end
  580.    
  581.     if fdict == 1 then
  582.         runtime_error("FIX:TODO - FDICT not currently implemented")
  583.         local dictid_ = bs:read(32)
  584.     end
  585.    
  586.     return window_size
  587. end
  588.  
  589. local function parse_huffmantables(bs)
  590.         local hlit = bs:read(5)  -- # of literal/length codes - 257
  591.         local hdist = bs:read(5) -- # of distance codes - 1
  592.         local hclen = noeof(bs:read(4)) -- # of code length codes - 4
  593.  
  594.         local ncodelen_codes = hclen + 4
  595.         local codelen_init = {}
  596.         local codelen_vals = {
  597.             16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
  598.         for i=1,ncodelen_codes do
  599.             local nbits = bs:read(3)
  600.             local val = codelen_vals[i]
  601.             codelen_init[val] = nbits
  602.         end
  603.         local codelentable = HuffmanTable(codelen_init, true)
  604.  
  605.         local function decode(ncodes)
  606.             local init = {}
  607.             local nbits
  608.             local val = 0
  609.             while val < ncodes do
  610.                 local codelen = codelentable:read(bs)
  611.                 --FIX:check nil?
  612.                 local nrepeat
  613.                 if codelen <= 15 then
  614.                     nrepeat = 1
  615.                     nbits = codelen
  616.                     --debug('w', nbits)
  617.                 elseif codelen == 16 then
  618.                     nrepeat = 3 + noeof(bs:read(2))
  619.                     -- nbits unchanged
  620.                 elseif codelen == 17 then
  621.                     nrepeat = 3 + noeof(bs:read(3))
  622.                     nbits = 0
  623.                 elseif codelen == 18 then
  624.                     nrepeat = 11 + noeof(bs:read(7))
  625.                     nbits = 0
  626.                 else
  627.                     error 'ASSERT'
  628.                 end
  629.                 for i=1,nrepeat do
  630.                     init[val] = nbits
  631.                     val = val + 1
  632.                 end
  633.             end
  634.             local huffmantable = HuffmanTable(init, true)
  635.             return huffmantable
  636.         end
  637.  
  638.         local nlit_codes = hlit + 257
  639.         local ndist_codes = hdist + 1
  640.  
  641.         local littable = decode(nlit_codes)
  642.         local disttable = decode(ndist_codes)
  643.  
  644.         return littable, disttable
  645. end
  646.  
  647.  
  648. local tdecode_len_base
  649. local tdecode_len_nextrabits
  650. local tdecode_dist_base
  651. local tdecode_dist_nextrabits
  652. local function parse_compressed_item(bs, outstate, littable, disttable)
  653.     local val = littable:read(bs)
  654.     --debug(val, val < 256 and string_char(val))
  655.     if val < 256 then -- literal
  656.         output(outstate, val)
  657.     elseif val == 256 then -- end of block
  658.         return true
  659.     else
  660.         if not tdecode_len_base then
  661.             local t = {[257]=3}
  662.             local skip = 1
  663.             for i=258,285,4 do
  664.                 for j=i,i+3 do t[j] = t[j-1] + skip end
  665.                 if i ~= 258 then skip = skip * 2 end
  666.             end
  667.             t[285] = 258
  668.             tdecode_len_base = t
  669.             --for i=257,285 do debug('T1',i,t[i]) end
  670.         end
  671.         if not tdecode_len_nextrabits then
  672.             local t = {}
  673.             if NATIVE_BITOPS then
  674.                 for i=257,285 do
  675.                     local j = math_max(i - 261, 0)
  676.                     t[i] = rshift(j, 2)
  677.                 end
  678.             else
  679.                 for i=257,285 do
  680.                     local j = math_max(i - 261, 0)
  681.                     t[i] = (j - (j % 4)) / 4
  682.                 end
  683.             end
  684.             t[285] = 0
  685.             tdecode_len_nextrabits = t
  686.             --for i=257,285 do debug('T2',i,t[i]) end
  687.         end
  688.         local len_base = tdecode_len_base[val]
  689.         local nextrabits = tdecode_len_nextrabits[val]
  690.         local extrabits = bs:read(nextrabits)
  691.         local len = len_base + extrabits
  692.  
  693.         if not tdecode_dist_base then
  694.             local t = {[0]=1}
  695.             local skip = 1
  696.             for i=1,29,2 do
  697.                 for j=i,i+1 do t[j] = t[j-1] + skip end
  698.                 if i ~= 1 then skip = skip * 2 end
  699.             end
  700.             tdecode_dist_base = t
  701.             --for i=0,29 do debug('T3',i,t[i]) end
  702.         end
  703.         if not tdecode_dist_nextrabits then
  704.             local t = {}
  705.             if NATIVE_BITOPS then
  706.                 for i=0,29 do
  707.                     local j = math_max(i - 2, 0)
  708.                     t[i] = rshift(j, 1)
  709.                 end
  710.             else
  711.                 for i=0,29 do
  712.                     local j = math_max(i - 2, 0)
  713.                     t[i] = (j - (j % 2)) / 2
  714.                 end
  715.             end
  716.             tdecode_dist_nextrabits = t
  717.             --for i=0,29 do debug('T4',i,t[i]) end
  718.         end
  719.         local dist_val = disttable:read(bs)
  720.         local dist_base = tdecode_dist_base[dist_val]
  721.         local dist_nextrabits = tdecode_dist_nextrabits[dist_val]
  722.         local dist_extrabits = bs:read(dist_nextrabits)
  723.         local dist = dist_base + dist_extrabits
  724.  
  725.         --debug('BACK', len, dist)
  726.         for i=1,len do
  727.             local pos = (outstate.window_pos - 1 - dist) % 32768 + 1  -- 32K
  728.             output(outstate, assert(outstate.window[pos], 'invalid distance'))
  729.         end
  730.     end
  731.     return false
  732. end
  733.  
  734.  
  735. local function parse_block(bs, outstate)
  736.     local bfinal = bs:read(1)
  737.     local btype = bs:read(2)
  738.  
  739.     local BTYPE_NO_COMPRESSION = 0
  740.     local BTYPE_FIXED_HUFFMAN = 1
  741.     local BTYPE_DYNAMIC_HUFFMAN = 2
  742.     local BTYPE_RESERVED_ = 3
  743.  
  744.     if DEBUG then
  745.         debug('bfinal=', bfinal)
  746.         debug('btype=', btype)
  747.     end
  748.  
  749.     if btype == BTYPE_NO_COMPRESSION then
  750.         bs:read(bs:nbits_left_in_byte())
  751.         local len = bs:read(16)
  752.         local nlen_ = noeof(bs:read(16))
  753.  
  754.         for i=1,len do
  755.             local by = noeof(bs:read(8))
  756.             output(outstate, by)
  757.         end
  758.     elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then
  759.         local littable, disttable
  760.         if btype == BTYPE_DYNAMIC_HUFFMAN then
  761.             littable, disttable = parse_huffmantables(bs)
  762.         else
  763.             littable  = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil}
  764.             disttable = HuffmanTable {0,5, 32,nil}
  765.         end
  766.  
  767.         repeat
  768.             local is_done = parse_compressed_item(
  769.                 bs, outstate, littable, disttable)
  770.         until is_done
  771.     else
  772.         runtime_error('unrecognized compression type '..btype)
  773.     end
  774.  
  775.     return bfinal ~= 0
  776. end
  777.  
  778.  
  779. function inflate(t)
  780.     local bs = get_bitstream(t.input)
  781.     local outbs = get_obytestream(t.output)
  782.     local outstate = make_outstate(outbs)
  783.  
  784.     repeat
  785.         local is_final = parse_block(bs, outstate)
  786.     until is_final
  787. end
  788. local inflate = inflate
  789.  
  790.  
  791. function gunzip(t)
  792.     local bs = get_bitstream(t.input)
  793.     local outbs = get_obytestream(t.output)
  794.     local disable_crc = t.disable_crc
  795.     if disable_crc == nil then disable_crc = false end
  796.  
  797.     parse_gzip_header(bs)
  798.  
  799.     local data_crc32 = 0
  800.  
  801.     inflate{input=bs, output=
  802.         disable_crc and outbs or
  803.             function(byte)
  804.                 data_crc32 = crc32(byte, data_crc32)
  805.                 outbs(byte)
  806.             end
  807.     }
  808.  
  809.     bs:read(bs:nbits_left_in_byte())
  810.  
  811.     local expected_crc32 = bs:read(32)
  812.     local isize = bs:read(32) -- ignored
  813.     if DEBUG then
  814.         debug('crc32=', expected_crc32)
  815.         debug('isize=', isize)
  816.     end
  817.     if not disable_crc and data_crc32 then
  818.         if data_crc32 ~= expected_crc32 then
  819.             runtime_error('invalid compressed data--crc error')
  820.         end    
  821.     end
  822.     if bs:read() then
  823.         warn 'trailing garbage ignored'
  824.     end
  825. end
  826.  
  827.  
  828. function adler32(byte, crc)
  829.     local s1 = crc % 65536
  830.     local s2 = (crc - s1) / 65536
  831.     s1 = (s1 + byte) % 65521
  832.     s2 = (s2 + s1) % 65521
  833.     return s2*65536 + s1
  834. end -- 65521 is the largest prime smaller than 2^16
  835.  
  836.  
  837. function inflate_zlib(t)
  838.     local bs = get_bitstream(t.input)
  839.     local outbs = get_obytestream(t.output)
  840.     local disable_crc = t.disable_crc
  841.     if disable_crc == nil then disable_crc = false end
  842.    
  843.     local window_size_ = parse_zlib_header(bs)
  844.    
  845.     local data_adler32 = 1
  846.    
  847.     inflate{input=bs, output=
  848.         disable_crc and outbs or
  849.             function(byte)
  850.                 data_adler32 = M.adler32(byte, data_adler32)
  851.                 outbs(byte)
  852.             end
  853.     }
  854.  
  855.     bs:read(bs:nbits_left_in_byte())
  856.    
  857.     local b3 = bs:read(8)
  858.     local b2 = bs:read(8)
  859.     local b1 = bs:read(8)
  860.     local b0 = bs:read(8)
  861.     local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0
  862.     if DEBUG then
  863.         debug('alder32=', expected_adler32)
  864.     end
  865.     if not disable_crc then
  866.         if data_adler32 ~= expected_adler32 then
  867.             runtime_error('invalid compressed data--crc error')
  868.         end    
  869.     end
  870.     if bs:read() then
  871.         warn 'trailing garbage ignored'
  872.     end
  873. end
  874.  
  875. --------------------------------------------------------------------------------------------------------
  876. -- END DEFLATE
  877. -- BEGIN ZIP
  878. --------------------------------------------------------------------------------------------------------
  879.  
  880. local function noCompression(t)
  881.     local fIn
  882.     if type(t.input) == "function" then
  883.         fIn = t.input
  884.     elseif type(t.input) == "table" and t.input.read then
  885.         fIn = t.input.read
  886.     end
  887.  
  888.     local fOut
  889.     if type(t.output) == "function" then
  890.         fOut = t.output
  891.     elseif type(t.output) == "table" and t.output.write then
  892.         fOut = t.output.write
  893.     end
  894.  
  895.     for c in fIn do
  896.         fOut(c:byte())
  897.     end
  898. end
  899.  
  900. local function assert(condition, errMsg, level)
  901.     if condition then return condition end
  902.     if type(level) ~= "number" then
  903.         level = 2
  904.     elseif level <= 0 then
  905.         level = 0
  906.     else
  907.         level = level + 1
  908.     end
  909.     error(errMsg or "Assertion failed!", level)
  910. end
  911.  
  912. function open(fileHandle)
  913.     local obj = {}
  914.     local data = {}
  915.     local cursor = 1
  916.     local endOfCentralDirectoryRecord
  917.     local centralDirectoryHeaders = {}
  918.  
  919.     local function ux(x)
  920.         local n = 0
  921.         for i=1, x do
  922.             n = n + data[cursor] * 2^((i-1) * 8)
  923.             cursor = cursor + 1
  924.         end
  925.         return n
  926.     end
  927.  
  928.     local function u1()
  929.         return ux(1)
  930.     end
  931.  
  932.     local function u2()
  933.         return ux(2)
  934.     end
  935.  
  936.     local function u4()
  937.         return ux(4)
  938.     end
  939.  
  940.     local function str(len)
  941.         local s = ""
  942.         for i=1,len or 1 do
  943.             s = s .. string.char(u1())
  944.         end
  945.         return s
  946.     end
  947.  
  948.     local function dataDescriptor()
  949.         local dataDescriptor = {}
  950.         local header = u4() -- optional header
  951.         if header == 0x08074b50 then
  952.             dataDescriptor.CRC_32 = u4()
  953.         else
  954.             dataDescriptor.CRC_32 = header
  955.         end
  956.         dataDescriptor.compressedSize = u4()
  957.         dataDescriptor.unCompressedSize = u4()
  958.     end
  959.  
  960.     local function parseFileHeader() -- 0x04034b50
  961.         local fileHeader = {}
  962.         assert(u4() == 0x04034b50, "Invalid local file header")
  963.         fileHeader.version = u2()
  964.         fileHeader.generalPurposeBitFlag = u2()
  965.         fileHeader.compressionMethod = u2()
  966.         fileHeader.fileLastModificationTime = u2()
  967.         fileHeader.fileLastModificationDate = u2()
  968.         fileHeader.CRC_32 = u4()
  969.         fileHeader.compressedSize = u4()
  970.         fileHeader.unCompressedSize = u4()
  971.         fileHeader.fileNameLength = u2()
  972.         fileHeader.extraFieldLength = u2()
  973.         fileHeader.fileName = str(fileHeader.fileNameLength)
  974.         fileHeader.extraField = str(fileHeader.extraFieldLength)
  975.  
  976.         fileHeader.compressedData = str(fileHeader.compressedSize)
  977.  
  978.         if bit.band(fileHeader.generalPurposeBitFlag, 8) > 0 then
  979.             fileHeader.dataDescriptor = dataDescriptor()
  980.         end
  981.  
  982.         return fileHeader
  983.     end
  984.  
  985.     local function parseCentralDirectoryHeader()
  986.         local header = {}
  987.         header.versionMadeBy = u2()
  988.         header.versionNeededToExtract = u2()
  989.         header.generalPurposeBitFlag = u2()
  990.         header.compressionMethod = u2()
  991.         header.fileLastModificationTime = u2()
  992.         header.fileLastModificationDate = u2()
  993.         header.CRC_32 = u4()
  994.         header.compressedSize = u4()
  995.         header.unCompressedSize = u4()
  996.         header.fileNameLength = u2()
  997.         header.extraFieldLength = u2()
  998.         header.commentLength = u2()
  999.         header.diskNumber = u2()
  1000.         header.internalFileAttributes = u2()
  1001.         header.externalFileAttributes = u4()
  1002.         header.offset = u4()
  1003.         header.fileName = str(header.fileNameLength)
  1004.         header.extraField = str(header.extraFieldLength)
  1005.         header.comment = str(header.commentLength)
  1006.         return header
  1007.     end
  1008.  
  1009.     local function parseEndOfCentralDirectoryRecord()
  1010.         local record = {}
  1011.         record.numberOfThisDisk = u2()
  1012.         record.centralDirectoryDisk = u2()
  1013.         record.numRecordsOnThisDisk = u2()
  1014.         record.numRecords = u2()
  1015.         record.sizeOfCentralDirectory = u4()
  1016.         record.offsetOfCentralDirectory = u4()
  1017.         record.commentLength = u2()
  1018.         record.comment = str(record.commentLength)
  1019.         return record
  1020.     end
  1021.  
  1022.     local function findCentralDirectoryHeader(path)
  1023.         path = fs.combine(path, "")
  1024.         for i,v in ipairs(centralDirectoryHeaders) do
  1025.             if fs.combine(v.fileName, "") == path then
  1026.                 return v
  1027.             end
  1028.         end
  1029.     end
  1030.  
  1031.     for b in fileHandle.read do
  1032.         table.insert(data, b)
  1033.     end
  1034.  
  1035.     cursor = #data - 3
  1036.     repeat
  1037.         local header = u4()
  1038.         cursor = cursor - 5
  1039.     until header == 0x06054b50
  1040.     cursor = cursor + 5
  1041.  
  1042.     endOfCentralDirectoryRecord = parseEndOfCentralDirectoryRecord()
  1043.  
  1044.     cursor = endOfCentralDirectoryRecord.offsetOfCentralDirectory + 1
  1045.     while u4() == 0x02014b50 do
  1046.         table.insert(centralDirectoryHeaders, parseCentralDirectoryHeader())
  1047.     end
  1048.  
  1049.     local zfs = {}
  1050.  
  1051.     function zfs.exists(path)
  1052.         return findCentralDirectoryHeader(fs.combine(path, "")) and true or false
  1053.     end
  1054.  
  1055.     function zfs.isDir(path)
  1056.         assert(type(path) == "string", "Expected string, got " .. type(path), 2)
  1057.         if path == "" or path == "/" then
  1058.             return true
  1059.         end
  1060.         local header = findCentralDirectoryHeader(path)
  1061.         return (header ~= nil) and header.fileName:sub(-1) == "/"
  1062.     end
  1063.  
  1064.     function zfs.list(path)
  1065.         path = fs.combine(path, "")
  1066.         assert(not zfs.exists(path) or zfs.isDir(path), "Not a directory", 2)
  1067.         local list = {}
  1068.         for i,v in ipairs(centralDirectoryHeaders) do
  1069.             local vpath = fs.combine(v.fileName, "")
  1070.             if path ~= vpath then
  1071.                 if fs.getDir(vpath) == path then
  1072.                     table.insert(list, fs.combine(vpath:sub(#path + 1), ""))
  1073.                 end
  1074.             end
  1075.         end
  1076.         return list
  1077.     end
  1078.  
  1079.     function zfs.open(path, mode)
  1080.         assert(mode == "r" or mode == "rb", "Invalid open mode: " .. tostring(mode), 2)
  1081.         if not zfs.exists(path) then
  1082.             return
  1083.         end
  1084.  
  1085.         local outData = {}
  1086.         local cdHeader = findCentralDirectoryHeader(fs.combine(path, ""))
  1087.         cursor = cdHeader.offset + 1
  1088.         local localFileHeader = parseFileHeader()
  1089.         assert(localFileHeader.compressionMethod == 8 or localFileHeader.compressionMethod == 0,
  1090.             "Unsupported compression method", 2)
  1091.         local compressedDataCursor = 1
  1092.         local decompressor
  1093.         if localFileHeader.compressionMethod == 8 then
  1094.             decompressor = inflate
  1095.         elseif localFileHeader.compressionMethod == 0 then
  1096.             decompressor = noCompression
  1097.         end
  1098.         decompressor{input=function()
  1099.             if compressedDataCursor > localFileHeader.compressedSize then
  1100.                 return
  1101.             end
  1102.             compressedDataCursor = compressedDataCursor + 1
  1103.             return localFileHeader.compressedData:sub(compressedDataCursor-1,compressedDataCursor-1)
  1104.         end, output=function(byte)
  1105.             if mode == "rb" then
  1106.                 table.insert(outData, byte)
  1107.             elseif mode == "r" then
  1108.                 table.insert(outData, string.char(byte))
  1109.             end
  1110.         end}
  1111.  
  1112.         local fh = {}
  1113.         if mode == "rb" then
  1114.             function fh.read()
  1115.                 return table.remove(outData, 1)
  1116.             end
  1117.         elseif mode == "r" then
  1118.             function fh.readLine()
  1119.                 local s = ""
  1120.                 for c in function() return table.remove(outData, 1) end do
  1121.                     if c == "\n" then
  1122.                         break
  1123.                     end
  1124.                     s = s .. c
  1125.                 end
  1126.                 return s
  1127.             end
  1128.  
  1129.             function fh.readAll()
  1130.                 local s = table.concat(outData)
  1131.                 outData = {}
  1132.                 return s
  1133.             end
  1134.         end
  1135.         function fh.close()
  1136.             outData = {}
  1137.         end
  1138.         return fh
  1139.     end
  1140.     return zfs
  1141. end
  1142.     end
  1143.     setfenv(zip_api_make, zip)
  1144.     zip_api_make()
  1145.  
  1146.     local function json_api_make()
  1147.         ------------------------------------------------------------------ utils
  1148. local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\"}
  1149.  
  1150. local function isArray(t)
  1151.     local max = 0
  1152.     for k,v in pairs(t) do
  1153.         if type(k) ~= "number" then
  1154.             return false
  1155.         elseif k > max then
  1156.             max = k
  1157.         end
  1158.     end
  1159.     return max == #t
  1160. end
  1161.  
  1162. local whites = {['\n']=true; ['\r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true}
  1163. function removeWhite(str)
  1164.     while whites[str:sub(1, 1)] do
  1165.         str = str:sub(2)
  1166.     end
  1167.     return str
  1168. end
  1169.  
  1170. ------------------------------------------------------------------ encoding
  1171.  
  1172. local function encodeCommon(val, pretty, tabLevel, tTracking)
  1173.     local str = ""
  1174.  
  1175.     -- Tabbing util
  1176.     local function tab(s)
  1177.         str = str .. ("\t"):rep(tabLevel) .. s
  1178.     end
  1179.  
  1180.     local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc)
  1181.         str = str .. bracket
  1182.         if pretty then
  1183.             str = str .. "\n"
  1184.             tabLevel = tabLevel + 1
  1185.         end
  1186.         for k,v in iterator(val) do
  1187.             tab("")
  1188.             loopFunc(k,v)
  1189.             str = str .. ","
  1190.             if pretty then str = str .. "\n" end
  1191.         end
  1192.         if pretty then
  1193.             tabLevel = tabLevel - 1
  1194.         end
  1195.         if str:sub(-2) == ",\n" then
  1196.             str = str:sub(1, -3) .. "\n"
  1197.         elseif str:sub(-1) == "," then
  1198.             str = str:sub(1, -2)
  1199.         end
  1200.         tab(closeBracket)
  1201.     end
  1202.  
  1203.     -- Table encoding
  1204.     if type(val) == "table" then
  1205.         assert(not tTracking[val], "Cannot encode a table holding itself recursively")
  1206.         tTracking[val] = true
  1207.         if isArray(val) then
  1208.             arrEncoding(val, "[", "]", ipairs, function(k,v)
  1209.                 str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
  1210.             end)
  1211.         else
  1212.             arrEncoding(val, "{", "}", pairs, function(k,v)
  1213.                 assert(type(k) == "string", "JSON object keys must be strings", 2)
  1214.                 str = str .. encodeCommon(k, pretty, tabLevel, tTracking)
  1215.                 str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking)
  1216.             end)
  1217.         end
  1218.     -- String encoding
  1219.     elseif type(val) == "string" then
  1220.         str = '"' .. val:gsub("[%c\"\\]", controls) .. '"'
  1221.     -- Number encoding
  1222.     elseif type(val) == "number" or type(val) == "boolean" then
  1223.         str = tostring(val)
  1224.     else
  1225.         error("JSON only supports arrays, objects, numbers, booleans, and strings", 2)
  1226.     end
  1227.     return str
  1228. end
  1229.  
  1230. function encode(val)
  1231.     return encodeCommon(val, false, 0, {})
  1232. end
  1233.  
  1234. function encodePretty(val)
  1235.     return encodeCommon(val, true, 0, {})
  1236. end
  1237.  
  1238. ------------------------------------------------------------------ decoding
  1239.  
  1240. local decodeControls = {}
  1241. for k,v in pairs(controls) do
  1242.     decodeControls[v] = k
  1243. end
  1244.  
  1245. function parseBoolean(str)
  1246.     if str:sub(1, 4) == "true" then
  1247.         return true, removeWhite(str:sub(5))
  1248.     else
  1249.         return false, removeWhite(str:sub(6))
  1250.     end
  1251. end
  1252.  
  1253. function parseNull(str)
  1254.     return nil, removeWhite(str:sub(5))
  1255. end
  1256.  
  1257. local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true}
  1258. function parseNumber(str)
  1259.     local i = 1
  1260.     while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do
  1261.         i = i + 1
  1262.     end
  1263.     local val = tonumber(str:sub(1, i - 1))
  1264.     str = removeWhite(str:sub(i))
  1265.     return val, str
  1266. end
  1267.  
  1268. function parseString(str)
  1269.     str = str:sub(2)
  1270.     local s = ""
  1271.     while str:sub(1,1) ~= "\"" do
  1272.         local next = str:sub(1,1)
  1273.         str = str:sub(2)
  1274.         assert(next ~= "\n", "Unclosed string")
  1275.  
  1276.         if next == "\\" then
  1277.             local escape = str:sub(1,1)
  1278.             str = str:sub(2)
  1279.  
  1280.             next = assert(decodeControls[next..escape], "Invalid escape character")
  1281.         end
  1282.  
  1283.         s = s .. next
  1284.     end
  1285.     return s, removeWhite(str:sub(2))
  1286. end
  1287.  
  1288. function parseArray(str)
  1289.     str = removeWhite(str:sub(2))
  1290.  
  1291.     local val = {}
  1292.     local i = 1
  1293.     while str:sub(1, 1) ~= "]" do
  1294.         local v = nil
  1295.         v, str = parseValue(str)
  1296.         val[i] = v
  1297.         i = i + 1
  1298.         str = removeWhite(str)
  1299.     end
  1300.     str = removeWhite(str:sub(2))
  1301.     return val, str
  1302. end
  1303.  
  1304. function parseObject(str)
  1305.     str = removeWhite(str:sub(2))
  1306.  
  1307.     local val = {}
  1308.     while str:sub(1, 1) ~= "}" do
  1309.         local k, v = nil, nil
  1310.         k, v, str = parseMember(str)
  1311.         val[k] = v
  1312.         str = removeWhite(str)
  1313.     end
  1314.     str = removeWhite(str:sub(2))
  1315.     return val, str
  1316. end
  1317.  
  1318. function parseMember(str)
  1319.     local k = nil
  1320.     k, str = parseValue(str)
  1321.     local val = nil
  1322.     val, str = parseValue(str)
  1323.     return k, val, str
  1324. end
  1325.  
  1326. function parseValue(str)
  1327.     local fchar = str:sub(1, 1)
  1328.     if fchar == "{" then
  1329.         return parseObject(str)
  1330.     elseif fchar == "[" then
  1331.         return parseArray(str)
  1332.     elseif tonumber(fchar) ~= nil or numChars[fchar] then
  1333.         return parseNumber(str)
  1334.     elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then
  1335.         return parseBoolean(str)
  1336.     elseif fchar == "\"" then
  1337.         return parseString(str)
  1338.     elseif str:sub(1, 4) == "null" then
  1339.         return parseNull(str)
  1340.     end
  1341.     return nil
  1342. end
  1343.  
  1344. function decode(str)
  1345.     str = removeWhite(str)
  1346.     t = parseValue(str)
  1347.     return t
  1348. end
  1349.  
  1350. function decodeFromFile(path)
  1351.     local file = assert(fs.open(path, "r"))
  1352.     local decoded = decode(file.readAll())
  1353.     file.close()
  1354.     return decoded
  1355. end
  1356.  
  1357.     end
  1358.     setfenv(json_api_make, json)
  1359.     json_api_make()
  1360.  
  1361.     local function base64_api_make()
  1362.         local lookup_V2C = {}
  1363. for ixChar = 65, 90 do lookup_V2C[ixChar - 65] = string.char(ixChar) end
  1364. for ixChar = 97, 122 do lookup_V2C[ixChar - 71] = string.char(ixChar) end
  1365. for ixChar = 48, 57 do lookup_V2C[ixChar + 4] = string.char(ixChar) end
  1366. lookup_V2C[62] = "+"
  1367. lookup_V2C[63] = "/"
  1368. local lookup_C2V = {}
  1369. for key, value in pairs(lookup_V2C) do lookup_C2V[value] = key end
  1370. lookup_C2V["="] = -1
  1371.  
  1372. function decode(str)
  1373.     local data = {}
  1374.     for i=1,#str do
  1375.         data[i] = lookup_C2V[str:sub(i,i)]
  1376.     end
  1377.     local result = {}
  1378.     local oldTime = os.time()
  1379.     for i=1, #str, 4 do
  1380.         local newTime = os.time()
  1381.         if newTime - oldTime >= (0.020 * 3) then
  1382.             oldTime = newTime
  1383.             sleep(0)
  1384.         end
  1385.         if data[i + 3] == -1 then
  1386.             if data[i + 2] == -1 then
  1387.                 table.insert(result, bit.blshift(data[i], 2) + bit.brshift(data[i + 1], 4))
  1388.             else
  1389.                 table.insert(result, bit.blshift(data[i], 2) + bit.brshift(data[i + 1], 4))
  1390.                 table.insert(result, bit.band(0xff, bit.blshift(data[i + 1], 4)) + bit.brshift(data[i + 2], 2))
  1391.             end
  1392.         else
  1393.             table.insert(result, bit.blshift(data[i], 2) + bit.brshift(data[i + 1], 4))
  1394.             table.insert(result, bit.band(0xff, bit.blshift(data[i + 1], 4)) + bit.brshift(data[i + 2], 2))
  1395.             table.insert(result, bit.band(0xff, bit.blshift(data[i + 2], 6)) + data[i + 3])
  1396.         end
  1397.     end
  1398.     return result
  1399. end
  1400.     end
  1401.     setfenv(base64_api_make, base64)
  1402.     base64_api_make()
  1403.  
  1404.     local function argparse_api_make()
  1405.         ------------------------------------------------------------------
  1406. -- Parameter
  1407. ------------------------------------------------------------------
  1408.  
  1409. local Parameter = {}
  1410. function Parameter:matches(arg, options, tArgs)
  1411.     if arg:sub(1,1) ~= "-" then
  1412.         return false
  1413.     end
  1414.     arg = arg:sub(2)
  1415.    
  1416.     if not (arg:find("^"..self.name.."$") or arg:find("^"..self.sShortcut.."$")) then
  1417.         return false
  1418.     end
  1419.  
  1420.     local val = table.remove(tArgs, 1)
  1421.  
  1422.     if self.isMulti then
  1423.         options[self.name] = options[self.name] or {}
  1424.         table.insert(options[self.name], val)
  1425.     else
  1426.         options[self.name] = val
  1427.     end
  1428.  
  1429.     return true
  1430. end
  1431.  
  1432. function Parameter:shortcut(shortcut)
  1433.     self.sShortcut = shortcut
  1434.     return self
  1435. end
  1436.  
  1437. function Parameter:multi()
  1438.     self.isMulti = true
  1439.     return self
  1440. end
  1441.  
  1442. ------------------------------------------------------------------
  1443. -- Switch
  1444. ------------------------------------------------------------------
  1445.  
  1446. local Switch = {}
  1447. function Switch:matches(arg, options, tArgs)
  1448.     if arg:sub(1,1) ~= "-" then
  1449.         return false
  1450.     end
  1451.     arg = arg:sub(2)
  1452.    
  1453.     if not (arg:find("^"..self.name.."$") or arg:find("^"..self.sShortcut.."$")) then
  1454.         return false
  1455.     end
  1456.  
  1457.     options[self.name] = true
  1458.     return true
  1459. end
  1460.  
  1461. function Switch:shortcut(shortcut)
  1462.     self.sShortcut = shortcut
  1463.     return self
  1464. end
  1465.  
  1466. ------------------------------------------------------------------
  1467. -- Argument
  1468. ------------------------------------------------------------------
  1469.  
  1470. local Argument = {}
  1471. function Argument:matches(arg, options, tArgs)
  1472.     if self.matched then
  1473.         return false
  1474.     end
  1475.  
  1476.     if self.nCount == 1 then
  1477.         options[self.name] = arg
  1478.     else
  1479.         local count = self.nCount
  1480.         if count == "*" then
  1481.             count = #tArgs
  1482.         else
  1483.             count = count - 1
  1484.         end
  1485.         local args = {arg}
  1486.         for i=1, count do
  1487.             table.insert(args, table.remove(tArgs, 1))
  1488.         end
  1489.         options[self.name] = args
  1490.     end
  1491.  
  1492.     self.matched = true
  1493.     return true
  1494. end
  1495.  
  1496. function Argument:count(count)
  1497.     assert(type(count) == "number" or count == "*", "Bad argument to Argument:count. Expected number, got " .. count)
  1498.     self.nCount = count
  1499.     return self
  1500. end
  1501.  
  1502. ------------------------------------------------------------------
  1503. -- Parser
  1504. ------------------------------------------------------------------
  1505.  
  1506. local Parser = {}
  1507. function Parser:parameter(name)
  1508.     local param = setmetatable({name=name,sShortcut=name}, {__index=Parameter})
  1509.     table.insert(self.parameters, param)
  1510.     return param
  1511. end
  1512.  
  1513. function Parser:switch(name)
  1514.     local switch = setmetatable({name=name,sShortcut=name}, {__index=Switch})
  1515.     table.insert(self.switches, switch)
  1516.     return switch
  1517. end
  1518.  
  1519. function Parser:argument(name)
  1520.     local arg = setmetatable({name=name,nCount=1}, {__index=Argument})
  1521.     table.insert(self.arguments, arg)
  1522.     return arg
  1523. end
  1524.  
  1525. function Parser:usage(str)
  1526.     self.sUsage = str
  1527. end
  1528.  
  1529. function Parser:printUsage()
  1530.     print(self.sUsage)
  1531. end
  1532.  
  1533. function Parser:parseArg(arg, options, tArgs)
  1534.     for i,v in ipairs(self.parameters) do
  1535.         if v:matches(arg, options, tArgs) then
  1536.             return true
  1537.         end
  1538.     end
  1539.     for i,v in ipairs(self.switches) do
  1540.         if v:matches(arg, options, tArgs) then
  1541.             return true
  1542.         end
  1543.     end
  1544.     for i,v in ipairs(self.arguments) do
  1545.         if v:matches(arg, options, tArgs) then
  1546.             return true
  1547.         end
  1548.     end
  1549.     return false
  1550. end
  1551.  
  1552. function Parser:parse(options, ...)
  1553.     local tArgs = {...}
  1554.     for arg in function() return table.remove(tArgs, 1) end do
  1555.         if not self:parseArg(arg, options, tArgs) then
  1556.             print(tArgs.error or ("Unknown argument: "..arg))
  1557.             self:printUsage()
  1558.             return false
  1559.         end
  1560.     end
  1561.     return options
  1562. end
  1563.  
  1564. function new()
  1565.     local parser = setmetatable({parameters={},switches={},arguments={}}, {__index=Parser})
  1566.     return parser
  1567. end
  1568.     end
  1569.     setfenv(argparse_api_make, argparse)
  1570.     argparse_api_make()
  1571. end
  1572.  
  1573. local oldTime = os.time()
  1574. local function sleepCheckin()
  1575.     local newTime = os.time()
  1576.     if newTime - oldTime >= (0.020 * 1.5) then
  1577.         oldTime = newTime
  1578.         sleep(0)
  1579.     end
  1580. end
  1581.  
  1582. local function combine(path, ...)
  1583.     if not path then
  1584.         return ""
  1585.     end
  1586.     return fs.combine(path, combine(...))
  1587. end
  1588.  
  1589. -- Arguments
  1590.  
  1591. local parser = argparse.new()
  1592. parser
  1593.     :parameter"user"
  1594.     :shortcut"u"
  1595. parser
  1596.     :parameter"repo"
  1597.     :shortcut"r"
  1598. parser
  1599.     :parameter"tag"
  1600.     :shortcut"t"
  1601. parser
  1602.     :switch"emit-events"
  1603.     :shortcut"e"
  1604. parser
  1605.     :argument"dir"
  1606. local options = parser:parse({}, ...)
  1607. assert(options and options.user and options.repo and options.dir, "Usage: grin -user <user> -repo <repo> [-tag tag_name] <dir>")
  1608.  
  1609. local print = print
  1610. if options["emit-events"] then
  1611.     function print(...)
  1612.         local s = ""
  1613.         for i,v in ipairs({...}) do
  1614.             s = s .. tostring(v)
  1615.         end
  1616.         os.queueEvent("grin_install_status", s)
  1617.     end
  1618. end
  1619.  
  1620.  
  1621. -- Begin installation
  1622.  
  1623. local githubApiResponse = assert(http.get("https://api.github.com/repos/"..options.user.."/"..options.repo.."/releases"))
  1624. assert(githubApiResponse.getResponseCode() == 200, "Failed github response")
  1625. print("Got github response")
  1626. local githubApiJSON = json.decode(githubApiResponse.readAll())
  1627.  
  1628. assert(type(githubApiJSON) == "table", "Malformed response")
  1629.  
  1630. local release
  1631. if options.tag then
  1632.     for i,v in ipairs(githubApiJSON) do
  1633.         if v.tag_name == options.tag then
  1634.             release = v
  1635.             break
  1636.         end
  1637.     end
  1638.     assert(release, "Release " .. options.tag .. " not found")
  1639. else
  1640.     release = assert(githubApiJSON[1], "Latest release not found")
  1641. end
  1642.  
  1643. local assetUrl = assert(release.assets and release.assets[1] and release.assets[1].url, "Malformed response")
  1644.  
  1645. print("Got JSON")
  1646. local zipResponse = assert(http.get(assetUrl, {["Accept"]="application/octet-stream"}))
  1647. assert(zipResponse.getResponseCode() == 200 or zipResponse.getResponseCode() == 302, "Failed zip response")
  1648. local base64Str = zipResponse.readAll()
  1649.  
  1650. print("Decoding base64")
  1651. sleep(0)
  1652. local zipTbl = assert(base64.decode(base64Str), "Failed to decode base 64")
  1653. print("Zip scanned. Unarchiving...")
  1654. sleep(0)
  1655.  
  1656. local i = 0
  1657. local zfs = zip.open({read=function()
  1658.     sleepCheckin()
  1659.     i = i + 1
  1660.     return zipTbl[i]
  1661. end})
  1662.  
  1663. local function copyFilesFromDir(dir)
  1664.     for i,v in ipairs(zfs.list(dir)) do
  1665.         sleepCheckin()
  1666.         local fullPath = fs.combine(dir, v)
  1667.         if zfs.isDir(fullPath) then
  1668.             copyFilesFromDir(fullPath)
  1669.         else
  1670.             print("Copying file: " .. fullPath)
  1671.             local fh = fs.open(combine(shell.resolve(options.dir), fullPath), "wb")
  1672.             local zfh = zfs.open(fullPath, "rb")
  1673.             for b in zfh.read do
  1674.                 sleepCheckin()
  1675.                 fh.write(b)
  1676.             end
  1677.             fh.close()
  1678.             zfh.close()
  1679.         end
  1680.     end
  1681. end
  1682.  
  1683. copyFilesFromDir("")
  1684. print("grin installation complete")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement