HydrantHunter

gateLiaison 2.0

Jul 13th, 2015
2,665
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 86.58 KB | None | 0 0
  1. --[[  LanteaCraft      ]]--
  2. --[[      and SGCraft  ]]--
  3. --[[   Gate Liaison    ]]--
  4. --[[      by Dog       ]]--
  5. --[[ aka HydrantHunter ]]--
  6. --[[  with help from   ]]--
  7. --[[  AfterLifeLochie  ]]--
  8. --[[ pastebin NZrxstWF ]]--
  9. local glVer = "2.0.00"
  10. --[[
  11. Tested with/requires:
  12.   - Minecraft 1.7.10+ AND ComputerCraft 1.75+ || LanteaCraft LC2-16+ | OR | SGCraft1.11.x-mc1.7.10+
  13.   For setup and usage instructions, please visit http://tinyurl.com/jf3rjr7
  14.  
  15. Special thanks to: SquidDev     (AES encryption/decryption)
  16.                    Alex Kloss   (base64 encoder/decoder)
  17.  
  18. IMPORTANT NOTE:
  19.   - all of the following variables are set by the program as necessary
  20.   - editing any of them below will most likely cause unexpected results
  21. ]]--
  22. --# AUTOMATIC/STATIC CONFIGURATION (Part 1)
  23. local tArgs, marquee, gateMon, timerMon = { ... }, { }, { }, { }
  24. local hardware = { marquee = 0, gateMon = 0, timerMon = 0 }
  25. local modem, gate, thisGate, incomingAddress, fuelPercent, updateTimer, connectionTimer, ccLabel, netSend, netReceive, updateClientAndScreens, updatePercentages, lockChevron, hangUp, displayChevrons, displayStatus, drawElement
  26. local lcGate, noMon, outgoingAlarm, irisState, waitingForIris, wifiComms, staticDrawn, bAndW, autoDisengage, dialFromDHD, continueDialing, gateDiscon, spinTime = false, false, false, false, false, false, false, false, false, false, false, false, false
  27. local grayScale, fullColor, thisCC = (_CC_VERSION or _HOST), term.isColor(), tostring(os.getComputerID())
  28. local connectionClock, gateStatus, irisStatus, secureStatus, modemSide, dialAddress, callDirection, rsSide = "00:00:0", "QRY", "QRY", "allclear", "none", "none", "none", "none"
  29. local irisPercent, chevronNumber, connectionTime, sgRemoteAddrLen, MAXFUEL_LC, MAXFUEL_SG = 0, 0, 0, 0, 100, 200000
  30. local gateSettings = { DHD = 99999999 }
  31. local sgStates = {
  32.   Idle = "Idle";
  33.   Dialing = "Dialing";
  34.   Dialling = "Dialing";
  35.   Opening = "Dialing";
  36.   Connected = "Connected";
  37.   Closing = "Disconnecting";
  38.   Offline = "Offline";
  39. }
  40. --# Color Definitions (color terminal, b&w terminal, color monitors)
  41. local white = colors.white
  42. local silver = colors.lightGray
  43. local gray = colors.gray
  44. local black = colors.black
  45. --local brown = colors.brown
  46. local yellow = colors.yellow
  47. local orange = colors.orange
  48. local red = colors.red
  49. --local magenta = colors.magenta
  50. --local purple = colors.purple
  51. local blue = colors.blue
  52. local sky = colors.lightBlue
  53. local cyan = colors.cyan
  54. --local lime = colors.lime
  55. local green = colors.green
  56. if not fullColor then
  57.   silver = grayScale and colors.lightGray or colors.white
  58.   gray = grayScale and colors.gray or colors.black
  59.   --brown = colors.white
  60.   yellow = colors.white
  61.   orange = colors.white
  62.   red = colors.white
  63.   --magenta = colors.white
  64.   --purple = colors.white
  65.   blue = colors.black
  66.   sky = colors.white
  67.   cyan = colors.white
  68.   --lime = colors.white
  69.   green = colors.white
  70. end
  71. local mwhite = colors.white
  72. local msilver = colors.lightGray
  73. local mgray = colors.gray
  74. local mblack = colors.black
  75. --local mbrown = colors.brown
  76. local myellow = colors.yellow
  77. local morange = colors.orange
  78. local mred = colors.red
  79. --local mmagenta = colors.magenta
  80. --local mpurple = colors.purple
  81. local mblue = colors.blue
  82. local msky = colors.lightBlue
  83. local mcyan = colors.cyan
  84. --local mlime = colors.lime
  85. local mgreen = colors.green
  86. --# END AUTOMATIC/STATIC CONFIGURATION (Part 1)
  87.  
  88. -- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
  89. -- licensed under the terms of the LGPL2
  90. -- http://lua-users.org/wiki/BaseSixtyFour
  91. -- character table string
  92. local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  93. -- encoding
  94. local function encode(data)
  95.   return ((data:gsub('.', function(x)
  96.     local r,b='',x:byte()
  97.     for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
  98.     return r;
  99.   end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
  100.     if (#x < 6) then return '' end
  101.     local c=0
  102.     for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
  103.     return b:sub(c+1,c+1)
  104.   end)..({ '', '==', '=' })[#data%3+1])
  105. end
  106. -- decoding
  107. local function decode(data)
  108.   data = string.gsub(data, '[^'..b..'=]', '')
  109.   return (data:gsub('.', function(x)
  110.     if (x == '=') then return '' end
  111.     local r,f='',(b:find(x)-1)
  112.     for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
  113.     return r;
  114.   end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
  115.     if (#x ~= 8) then return '' end
  116.     local c=0
  117.     for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
  118.     return string.char(c)
  119.   end))
  120. end
  121.  
  122. -- AES Lua implementation by SquidDev
  123. -- https://gist.github.com/SquidDev/86925e07cbabd70773e53d781bd8b2fe
  124. -- http://pastebin.com/DMx8M0LP
  125. local encrypt, decrypt
  126. do
  127.   local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
  128.   local bit=_W(function(_ENV, ...)
  129.   --[[
  130.     This bit API is designed to cope with unsigned integers instead of normal integers
  131.     To do this we add checks for overflows: (x > 2^31 ? x - 2 ^ 32 : x)
  132.     These are written in long form because no constant folding.
  133.   ]]
  134.   local floor = math.floor
  135.   local lshift, rshift
  136.  
  137.   rshift = function(a,disp)
  138.     return floor(a % 4294967296 / 2^disp)
  139.   end
  140.  
  141.   lshift = function(a,disp)
  142.     return (a * 2^disp) % 4294967296
  143.   end
  144.  
  145.   return {
  146.     -- bit operations
  147.     bnot = bit32 and bit32.bnot or bit.bnot,
  148.     band = bit32 and bit32.band or bit.band,
  149.     bor  = bit32 and bit32.bor or bit.bor,
  150.     bxor = bit32 and bit32.bxor or bit.bxor,
  151.     rshift = rshift,
  152.     lshift = lshift,
  153.   }
  154.   end)
  155.  
  156.   local gf=_W(function(_ENV, ...)
  157.   -- finite field with base 2 and modulo irreducible polynom x^8+x^4+x^3+x+1 = 0x11d
  158.   local bxor = bit32 and bit32.bxor or bit.bxor
  159.   local lshift = bit.lshift
  160.   -- private data of gf
  161.   local n = 0x100
  162.   local ord = 0xff
  163.   local irrPolynom = 0x11b
  164.   local exp, log = {}, {}
  165.   --
  166.   -- add two polynoms (its simply xor)
  167.   --
  168.   local function add(operand1, operand2)
  169.     return bxor(operand1,operand2)
  170.   end
  171.   --
  172.   -- subtract two polynoms (same as addition)
  173.   --
  174.   local function sub(operand1, operand2)
  175.     return bxor(operand1,operand2)
  176.   end
  177.   --
  178.   -- inverts element
  179.   -- a^(-1) = g^(order - log(a))
  180.   --
  181.   local function invert(operand)
  182.     -- special case for 1 or normal invert
  183.     return operand == 1 and 1 or exp[ord - log[operand]]
  184.   end
  185.   --
  186.   -- multiply two elements using a logarithm table
  187.   -- a*b = g^(log(a)+log(b))
  188.   --
  189.   local function mul(operand1, operand2)
  190.     if (operand1 == 0 or operand2 == 0) then
  191.       return 0
  192.     end
  193.     local exponent = log[operand1] + log[operand2]
  194.     if (exponent >= ord) then
  195.       exponent = exponent - ord
  196.     end
  197.     return exp[exponent]
  198.   end
  199.   --
  200.   -- divide two elements
  201.   -- a/b = g^(log(a)-log(b))
  202.   --
  203.   local function div(operand1, operand2)
  204.     if (operand1 == 0)  then
  205.       return 0
  206.     end
  207.     -- TODO: exception if operand2 == 0
  208.     local exponent = log[operand1] - log[operand2]
  209.     if (exponent < 0) then
  210.       exponent = exponent + ord
  211.     end
  212.     return exp[exponent]
  213.   end
  214.   --
  215.   -- print logarithmic table
  216.   --
  217.   local function printLog()
  218.     for i = 1, n do
  219.       print("log(", i-1, ")=", log[i-1])
  220.     end
  221.   end
  222.   --
  223.   -- print exponentiation table
  224.   --
  225.   local function printExp()
  226.     for i = 1, n do
  227.       print("exp(", i-1, ")=", exp[i-1])
  228.     end
  229.   end
  230.   --
  231.   -- calculate logarithmic and exponentiation table
  232.   --
  233.   local function initMulTable()
  234.     local a = 1
  235.     for i = 0,ord-1 do
  236.       exp[i] = a
  237.       log[a] = i
  238.       -- multiply with generator x+1 -> left shift + 1
  239.       a = bxor(lshift(a, 1), a)
  240.       -- if a gets larger than order, reduce modulo irreducible polynom
  241.       if a > ord then
  242.         a = sub(a, irrPolynom)
  243.       end
  244.     end
  245.   end
  246.  
  247.   initMulTable()
  248.  
  249.   return {
  250.     add = add,
  251.     sub = sub,
  252.     invert = invert,
  253.     mul = mul,
  254.     div = div,
  255.     printLog = printLog,
  256.     printExp = printExp,
  257.   }
  258.   end)
  259.  
  260.   util=_W(function(_ENV, ...)
  261.   -- Cache some bit operators
  262.   local bxor = bit.bxor
  263.   local rshift = bit.rshift
  264.   local band = bit.band
  265.   local lshift = bit.lshift
  266.   local sleepCheckIn
  267.   --
  268.   -- calculate the parity of one byte
  269.   --
  270.   local function byteParity(byte)
  271.     byte = bxor(byte, rshift(byte, 4))
  272.     byte = bxor(byte, rshift(byte, 2))
  273.     byte = bxor(byte, rshift(byte, 1))
  274.     return band(byte, 1)
  275.   end
  276.   --
  277.   -- get byte at position index
  278.   --
  279.   local function getByte(number, index)
  280.     return index == 0 and band(number,0xff) or band(rshift(number, index*8),0xff)
  281.   end
  282.   --
  283.   -- put number into int at position index
  284.   --
  285.   local function putByte(number, index)
  286.     return index == 0 and band(number,0xff) or lshift(band(number,0xff),index*8)
  287.   end
  288.   --
  289.   -- convert byte array to int array
  290.   --
  291.   local function bytesToInts(bytes, start, n)
  292.     local ints = {}
  293.     for i = 0, n - 1 do
  294.       ints[i + 1] =
  295.           putByte(bytes[start + (i*4)], 3) +
  296.           putByte(bytes[start + (i*4) + 1], 2) +
  297.           putByte(bytes[start + (i*4) + 2], 1) +
  298.           putByte(bytes[start + (i*4) + 3], 0)
  299.       if n % 10000 == 0 then sleepCheckIn() end
  300.     end
  301.     return ints
  302.   end
  303.   --
  304.   -- convert int array to byte array
  305.   --
  306.   local function intsToBytes(ints, output, outputOffset, n)
  307.     n = n or #ints
  308.     for i = 0, n - 1 do
  309.       for j = 0,3 do
  310.         output[outputOffset + i*4 + (3 - j)] = getByte(ints[i + 1], j)
  311.       end
  312.       if n % 10000 == 0 then sleepCheckIn() end
  313.     end
  314.     return output
  315.   end
  316.   --
  317.   -- convert bytes to hexString
  318.   --
  319.   local function bytesToHex(bytes)
  320.     local hexBytes = ""
  321.     for i,byte in ipairs(bytes) do
  322.       hexBytes = hexBytes .. string.format("%02x ", byte)
  323.     end
  324.     return hexBytes
  325.   end
  326.  
  327.   local function hexToBytes(bytes)
  328.     local out = {}
  329.     for i = 1, #bytes, 2 do
  330.       out[#out + 1] = tonumber(bytes:sub(i, i + 1), 16)
  331.     end
  332.     return out
  333.   end
  334.   --
  335.   -- convert data to hex string
  336.   --
  337.   local function toHexString(data)
  338.     local type = type(data)
  339.     if (type == "number") then
  340.       return string.format("%08x",data)
  341.     elseif (type == "table") then
  342.       return bytesToHex(data)
  343.     elseif (type == "string") then
  344.       local bytes = {string.byte(data, 1, #data)}
  345.       return bytesToHex(bytes)
  346.     else
  347.       return data
  348.     end
  349.   end
  350.  
  351.   local function padByteString(data)
  352.     local dataLength = #data
  353.     local random1 = math.random(0,255)
  354.     local random2 = math.random(0,255)
  355.     local prefix = string.char(random1,
  356.       random2,
  357.       random1,
  358.       random2,
  359.       getByte(dataLength, 3),
  360.       getByte(dataLength, 2),
  361.       getByte(dataLength, 1),
  362.       getByte(dataLength, 0)
  363.     )
  364.     data = prefix .. data
  365.     local padding, paddingLength = "", math.ceil(#data/16)*16 - #data
  366.     for i=1,paddingLength do
  367.       padding = padding .. string.char(math.random(0,255))
  368.     end
  369.     return data .. padding
  370.   end
  371.  
  372.   local function properlyDecrypted(data)
  373.     local random = {string.byte(data,1,4)}
  374.     if (random[1] == random[3] and random[2] == random[4]) then
  375.       return true
  376.     end
  377.     return false
  378.   end
  379.  
  380.   local function unpadByteString(data)
  381.     if (not properlyDecrypted(data)) then
  382.       return nil
  383.     end
  384.     local dataLength = putByte(string.byte(data,5), 3)
  385.              + putByte(string.byte(data,6), 2)
  386.              + putByte(string.byte(data,7), 1)
  387.              + putByte(string.byte(data,8), 0)
  388.     return string.sub(data,9,8+dataLength)
  389.   end
  390.  
  391.   local function xorIV(data, iv)
  392.     for i = 1,16 do
  393.       data[i] = bxor(data[i], iv[i])
  394.     end
  395.   end
  396.  
  397.   local function increment(data)
  398.     local i = 16
  399.     while true do
  400.       local value = data[i] + 1
  401.       if value >= 256 then
  402.         data[i] = value - 256
  403.         i = (i - 2) % 16 + 1
  404.       else
  405.         data[i] = value
  406.         break
  407.       end
  408.     end
  409.   end
  410.  
  411.   -- Called every encryption cycle
  412.   local push, pull, time = os.queueEvent, coroutine.yield, os.time
  413.   local oldTime = time()
  414.   local function sleepCheckIn()
  415.     local newTime = time()
  416.     if newTime - oldTime >= 0.03 then -- (0.020 * 1.5)
  417.       oldTime = newTime
  418.       push("sleep")
  419.       pull("sleep")
  420.     end
  421.   end
  422.  
  423.   local function getRandomData(bytes)
  424.     local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
  425.     local result = {}
  426.     for i=1,bytes do
  427.       insert(result, random(0,255))
  428.       if i % 10240 == 0 then sleep() end
  429.     end
  430.     return result
  431.   end
  432.  
  433.   local function getRandomString(bytes)
  434.     local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
  435.     local result = {}
  436.     for i=1,bytes do
  437.       insert(result, char(random(0,255)))
  438.       if i % 10240 == 0 then sleep() end
  439.     end
  440.     return table.concat(result)
  441.   end
  442.  
  443.   return {
  444.     byteParity = byteParity,
  445.     getByte = getByte,
  446.     putByte = putByte,
  447.     bytesToInts = bytesToInts,
  448.     intsToBytes = intsToBytes,
  449.     bytesToHex = bytesToHex,
  450.     hexToBytes = hexToBytes,
  451.     toHexString = toHexString,
  452.     padByteString = padByteString,
  453.     properlyDecrypted = properlyDecrypted,
  454.     unpadByteString = unpadByteString,
  455.     xorIV = xorIV,
  456.     increment = increment,
  457.     sleepCheckIn = sleepCheckIn,
  458.     getRandomData = getRandomData,
  459.     getRandomString = getRandomString,
  460.   }
  461.   end)
  462.  
  463.   aes=_W(function(_ENV, ...)
  464.   -- Implementation of AES with nearly pure lua
  465.   -- AES with lua is slow, really slow :-)
  466.   local putByte = util.putByte
  467.   local getByte = util.getByte
  468.   -- some constants
  469.   local ROUNDS = 'rounds'
  470.   local KEY_TYPE = "type"
  471.   local ENCRYPTION_KEY=1
  472.   local DECRYPTION_KEY=2
  473.   -- aes SBOX
  474.   local SBox = {}
  475.   local iSBox = {}
  476.   -- aes tables
  477.   local table0 = {}
  478.   local table1 = {}
  479.   local table2 = {}
  480.   local table3 = {}
  481.   local tableInv0 = {}
  482.   local tableInv1 = {}
  483.   local tableInv2 = {}
  484.   local tableInv3 = {}
  485.   -- round constants
  486.   local rCon = {
  487.     0x01000000,
  488.     0x02000000,
  489.     0x04000000,
  490.     0x08000000,
  491.     0x10000000,
  492.     0x20000000,
  493.     0x40000000,
  494.     0x80000000,
  495.     0x1b000000,
  496.     0x36000000,
  497.     0x6c000000,
  498.     0xd8000000,
  499.     0xab000000,
  500.     0x4d000000,
  501.     0x9a000000,
  502.     0x2f000000,
  503.   }
  504.   --
  505.   -- affine transformation for calculating the S-Box of AES
  506.   --
  507.   local function affinMap(byte)
  508.     mask = 0xf8
  509.     result = 0
  510.     for i = 1,8 do
  511.       result = bit.lshift(result,1)
  512.       parity = util.byteParity(bit.band(byte,mask))
  513.       result = result + parity
  514.       -- simulate roll
  515.       lastbit = bit.band(mask, 1)
  516.       mask = bit.band(bit.rshift(mask, 1),0xff)
  517.       mask = lastbit ~= 0 and bit.bor(mask, 0x80) or bit.band(mask, 0x7f)
  518.     end
  519.     return bit.bxor(result, 0x63)
  520.   end
  521.   --
  522.   -- calculate S-Box and inverse S-Box of AES
  523.   -- apply affine transformation to inverse in finite field 2^8
  524.   --
  525.   local function calcSBox()
  526.     for i = 0, 255 do
  527.       inverse = i ~= 0 and gf.invert(i) or 0
  528.       mapped = affinMap(inverse)
  529.       SBox[i] = mapped
  530.       iSBox[mapped] = i
  531.     end
  532.   end
  533.   --
  534.   -- Calculate round tables
  535.   -- round tables are used to calculate shiftRow, MixColumn and SubBytes
  536.   -- with 4 table lookups and 4 xor operations.
  537.   --
  538.   local function calcRoundTables()
  539.     for x = 0,255 do
  540.       byte = SBox[x]
  541.       table0[x] = putByte(gf.mul(0x03, byte), 0)
  542.                 + putByte(             byte , 1)
  543.                 + putByte(             byte , 2)
  544.                 + putByte(gf.mul(0x02, byte), 3)
  545.       table1[x] = putByte(             byte , 0)
  546.                 + putByte(             byte , 1)
  547.                 + putByte(gf.mul(0x02, byte), 2)
  548.                 + putByte(gf.mul(0x03, byte), 3)
  549.       table2[x] = putByte(             byte , 0)
  550.                 + putByte(gf.mul(0x02, byte), 1)
  551.                 + putByte(gf.mul(0x03, byte), 2)
  552.                 + putByte(             byte , 3)
  553.       table3[x] = putByte(gf.mul(0x02, byte), 0)
  554.                 + putByte(gf.mul(0x03, byte), 1)
  555.                 + putByte(             byte , 2)
  556.                 + putByte(             byte , 3)
  557.     end
  558.   end
  559.   --
  560.   -- Calculate inverse round tables
  561.   -- does the inverse of the normal roundtables for the equivalent
  562.   -- decryption algorithm.
  563.   --
  564.   local function calcInvRoundTables()
  565.     for x = 0,255 do
  566.       byte = iSBox[x]
  567.       tableInv0[x] = putByte(gf.mul(0x0b, byte), 0)
  568.                  + putByte(gf.mul(0x0d, byte), 1)
  569.                  + putByte(gf.mul(0x09, byte), 2)
  570.                  + putByte(gf.mul(0x0e, byte), 3)
  571.       tableInv1[x] = putByte(gf.mul(0x0d, byte), 0)
  572.                  + putByte(gf.mul(0x09, byte), 1)
  573.                  + putByte(gf.mul(0x0e, byte), 2)
  574.                  + putByte(gf.mul(0x0b, byte), 3)
  575.       tableInv2[x] = putByte(gf.mul(0x09, byte), 0)
  576.                  + putByte(gf.mul(0x0e, byte), 1)
  577.                  + putByte(gf.mul(0x0b, byte), 2)
  578.                  + putByte(gf.mul(0x0d, byte), 3)
  579.       tableInv3[x] = putByte(gf.mul(0x0e, byte), 0)
  580.                  + putByte(gf.mul(0x0b, byte), 1)
  581.                  + putByte(gf.mul(0x0d, byte), 2)
  582.                  + putByte(gf.mul(0x09, byte), 3)
  583.     end
  584.   end
  585.   --
  586.   -- rotate word: 0xaabbccdd gets 0xbbccddaa
  587.   -- used for key schedule
  588.   --
  589.   local function rotWord(word)
  590.     local tmp = bit.band(word,0xff000000)
  591.     return (bit.lshift(word,8) + bit.rshift(tmp,24))
  592.   end
  593.   --
  594.   -- replace all bytes in a word with the SBox.
  595.   -- used for key schedule
  596.   --
  597.   local function subWord(word)
  598.     return putByte(SBox[getByte(word,0)],0)
  599.       + putByte(SBox[getByte(word,1)],1)
  600.       + putByte(SBox[getByte(word,2)],2)
  601.       + putByte(SBox[getByte(word,3)],3)
  602.   end
  603.   --
  604.   -- generate key schedule for aes encryption
  605.   --
  606.   -- returns table with all round keys and
  607.   -- the necessary number of rounds saved in [ROUNDS]
  608.   --
  609.   local function expandEncryptionKey(key)
  610.     local keySchedule = {}
  611.     local keyWords = math.floor(#key / 4)
  612.     if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then
  613.       error("Invalid key size: " .. tostring(keyWords))
  614.       return nil
  615.     end
  616.     keySchedule[ROUNDS] = keyWords + 6
  617.     keySchedule[KEY_TYPE] = ENCRYPTION_KEY
  618.     for i = 0,keyWords - 1 do
  619.       keySchedule[i] = putByte(key[i*4+1], 3)
  620.                + putByte(key[i*4+2], 2)
  621.                + putByte(key[i*4+3], 1)
  622.                + putByte(key[i*4+4], 0)
  623.     end
  624.     for i = keyWords, (keySchedule[ROUNDS] + 1)*4 - 1 do
  625.       local tmp = keySchedule[i-1]
  626.       if ( i % keyWords == 0) then
  627.         tmp = rotWord(tmp)
  628.         tmp = subWord(tmp)
  629.         local index = math.floor(i/keyWords)
  630.         tmp = bit.bxor(tmp,rCon[index])
  631.       elseif (keyWords > 6 and i % keyWords == 4) then
  632.         tmp = subWord(tmp)
  633.       end
  634.       keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp)
  635.     end
  636.     return keySchedule
  637.   end
  638.   --
  639.   -- Inverse mix column
  640.   -- used for key schedule of decryption key
  641.   --
  642.   local function invMixColumnOld(word)
  643.     local b0 = getByte(word,3)
  644.     local b1 = getByte(word,2)
  645.     local b2 = getByte(word,1)
  646.     local b3 = getByte(word,0)
  647.     return putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1),
  648.                          gf.mul(0x0d, b2)),
  649.                          gf.mul(0x09, b3)),
  650.                          gf.mul(0x0e, b0)),3)
  651.        + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2),
  652.                          gf.mul(0x0d, b3)),
  653.                          gf.mul(0x09, b0)),
  654.                          gf.mul(0x0e, b1)),2)
  655.        + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3),
  656.                          gf.mul(0x0d, b0)),
  657.                          gf.mul(0x09, b1)),
  658.                          gf.mul(0x0e, b2)),1)
  659.        + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0),
  660.                          gf.mul(0x0d, b1)),
  661.                          gf.mul(0x09, b2)),
  662.                          gf.mul(0x0e, b3)),0)
  663.   end
  664.   --
  665.   -- Optimized inverse mix column
  666.   -- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
  667.   -- TODO: make it work
  668.   --
  669.   local function invMixColumn(word)
  670.     local b0 = getByte(word,3)
  671.     local b1 = getByte(word,2)
  672.     local b2 = getByte(word,1)
  673.     local b3 = getByte(word,0)
  674.     local t = bit.bxor(b3,b2)
  675.     local u = bit.bxor(b1,b0)
  676.     local v = bit.bxor(t,u)
  677.     v = bit.bxor(v,gf.mul(0x08,v))
  678.     w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0)))
  679.     v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1)))
  680.     return putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0)
  681.        + putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t              )), 1)
  682.        + putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2)
  683.        + putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u              )), 3)
  684.   end
  685.   --
  686.   -- generate key schedule for aes decryption
  687.   --
  688.   -- uses key schedule for aes encryption and transforms each
  689.   -- key by inverse mix column.
  690.   --
  691.   local function expandDecryptionKey(key)
  692.     local keySchedule = expandEncryptionKey(key)
  693.     if (keySchedule == nil) then
  694.       return nil
  695.     end
  696.     keySchedule[KEY_TYPE] = DECRYPTION_KEY
  697.     for i = 4, (keySchedule[ROUNDS] + 1)*4 - 5 do
  698.       keySchedule[i] = invMixColumnOld(keySchedule[i])
  699.     end
  700.     return keySchedule
  701.   end
  702.   --
  703.   -- xor round key to state
  704.   --
  705.   local function addRoundKey(state, key, round)
  706.     for i = 0, 3 do
  707.       state[i + 1] = bit.bxor(state[i + 1], key[round*4+i])
  708.     end
  709.   end
  710.   --
  711.   -- do encryption round (ShiftRow, SubBytes, MixColumn together)
  712.   --
  713.   local function doRound(origState, dstState)
  714.     dstState[1] =  bit.bxor(bit.bxor(bit.bxor(
  715.           table0[getByte(origState[1],3)],
  716.           table1[getByte(origState[2],2)]),
  717.           table2[getByte(origState[3],1)]),
  718.           table3[getByte(origState[4],0)])
  719.     dstState[2] =  bit.bxor(bit.bxor(bit.bxor(
  720.           table0[getByte(origState[2],3)],
  721.           table1[getByte(origState[3],2)]),
  722.           table2[getByte(origState[4],1)]),
  723.           table3[getByte(origState[1],0)])
  724.     dstState[3] =  bit.bxor(bit.bxor(bit.bxor(
  725.           table0[getByte(origState[3],3)],
  726.           table1[getByte(origState[4],2)]),
  727.           table2[getByte(origState[1],1)]),
  728.           table3[getByte(origState[2],0)])
  729.     dstState[4] =  bit.bxor(bit.bxor(bit.bxor(
  730.           table0[getByte(origState[4],3)],
  731.           table1[getByte(origState[1],2)]),
  732.           table2[getByte(origState[2],1)]),
  733.           table3[getByte(origState[3],0)])
  734.   end
  735.   --
  736.   -- do last encryption round (ShiftRow and SubBytes)
  737.   --
  738.   local function doLastRound(origState, dstState)
  739.     dstState[1] = putByte(SBox[getByte(origState[1],3)], 3)
  740.           + putByte(SBox[getByte(origState[2],2)], 2)
  741.           + putByte(SBox[getByte(origState[3],1)], 1)
  742.           + putByte(SBox[getByte(origState[4],0)], 0)
  743.     dstState[2] = putByte(SBox[getByte(origState[2],3)], 3)
  744.           + putByte(SBox[getByte(origState[3],2)], 2)
  745.           + putByte(SBox[getByte(origState[4],1)], 1)
  746.           + putByte(SBox[getByte(origState[1],0)], 0)
  747.     dstState[3] = putByte(SBox[getByte(origState[3],3)], 3)
  748.           + putByte(SBox[getByte(origState[4],2)], 2)
  749.           + putByte(SBox[getByte(origState[1],1)], 1)
  750.           + putByte(SBox[getByte(origState[2],0)], 0)
  751.     dstState[4] = putByte(SBox[getByte(origState[4],3)], 3)
  752.           + putByte(SBox[getByte(origState[1],2)], 2)
  753.           + putByte(SBox[getByte(origState[2],1)], 1)
  754.           + putByte(SBox[getByte(origState[3],0)], 0)
  755.   end
  756.   --
  757.   -- do decryption round
  758.   --
  759.   local function doInvRound(origState, dstState)
  760.     dstState[1] =  bit.bxor(bit.bxor(bit.bxor(
  761.           tableInv0[getByte(origState[1],3)],
  762.           tableInv1[getByte(origState[4],2)]),
  763.           tableInv2[getByte(origState[3],1)]),
  764.           tableInv3[getByte(origState[2],0)])
  765.     dstState[2] =  bit.bxor(bit.bxor(bit.bxor(
  766.           tableInv0[getByte(origState[2],3)],
  767.           tableInv1[getByte(origState[1],2)]),
  768.           tableInv2[getByte(origState[4],1)]),
  769.           tableInv3[getByte(origState[3],0)])
  770.     dstState[3] =  bit.bxor(bit.bxor(bit.bxor(
  771.           tableInv0[getByte(origState[3],3)],
  772.           tableInv1[getByte(origState[2],2)]),
  773.           tableInv2[getByte(origState[1],1)]),
  774.           tableInv3[getByte(origState[4],0)])
  775.     dstState[4] =  bit.bxor(bit.bxor(bit.bxor(
  776.           tableInv0[getByte(origState[4],3)],
  777.           tableInv1[getByte(origState[3],2)]),
  778.           tableInv2[getByte(origState[2],1)]),
  779.           tableInv3[getByte(origState[1],0)])
  780.   end
  781.   --
  782.   -- do last decryption round
  783.   --
  784.   local function doInvLastRound(origState, dstState)
  785.     dstState[1] = putByte(iSBox[getByte(origState[1],3)], 3)
  786.           + putByte(iSBox[getByte(origState[4],2)], 2)
  787.           + putByte(iSBox[getByte(origState[3],1)], 1)
  788.           + putByte(iSBox[getByte(origState[2],0)], 0)
  789.     dstState[2] = putByte(iSBox[getByte(origState[2],3)], 3)
  790.           + putByte(iSBox[getByte(origState[1],2)], 2)
  791.           + putByte(iSBox[getByte(origState[4],1)], 1)
  792.           + putByte(iSBox[getByte(origState[3],0)], 0)
  793.     dstState[3] = putByte(iSBox[getByte(origState[3],3)], 3)
  794.           + putByte(iSBox[getByte(origState[2],2)], 2)
  795.           + putByte(iSBox[getByte(origState[1],1)], 1)
  796.           + putByte(iSBox[getByte(origState[4],0)], 0)
  797.     dstState[4] = putByte(iSBox[getByte(origState[4],3)], 3)
  798.           + putByte(iSBox[getByte(origState[3],2)], 2)
  799.           + putByte(iSBox[getByte(origState[2],1)], 1)
  800.           + putByte(iSBox[getByte(origState[1],0)], 0)
  801.   end
  802.   --
  803.   -- encrypts 16 Bytes
  804.   -- key           encryption key schedule
  805.   -- input         array with input data
  806.   -- inputOffset   start index for input
  807.   -- output        array for encrypted data
  808.   -- outputOffset  start index for output
  809.   --
  810.   local function encrypt(key, input, inputOffset, output, outputOffset)
  811.     --default parameters
  812.     inputOffset = inputOffset or 1
  813.     output = output or {}
  814.     outputOffset = outputOffset or 1
  815.     local state, tmpState = {}, {}
  816.     if (key[KEY_TYPE] ~= ENCRYPTION_KEY) then
  817.       error("No encryption key: " .. tostring(key[KEY_TYPE]) .. ", expected " .. ENCRYPTION_KEY)
  818.       return
  819.     end
  820.     state = util.bytesToInts(input, inputOffset, 4)
  821.     addRoundKey(state, key, 0)
  822.     local round = 1
  823.     while (round < key[ROUNDS] - 1) do
  824.       -- do a double round to save temporary assignments
  825.       doRound(state, tmpState)
  826.       addRoundKey(tmpState, key, round)
  827.       round = round + 1
  828.       doRound(tmpState, state)
  829.       addRoundKey(state, key, round)
  830.       round = round + 1
  831.     end
  832.     doRound(state, tmpState)
  833.     addRoundKey(tmpState, key, round)
  834.     round = round +1
  835.     doLastRound(tmpState, state)
  836.     addRoundKey(state, key, round)
  837.     util.sleepCheckIn()
  838.     return util.intsToBytes(state, output, outputOffset)
  839.   end
  840.   --
  841.   -- decrypt 16 bytes
  842.   -- key           decryption key schedule
  843.   -- input         array with input data
  844.   -- inputOffset   start index for input
  845.   -- output        array for decrypted data
  846.   -- outputOffset  start index for output
  847.   ---
  848.   local function decrypt(key, input, inputOffset, output, outputOffset)
  849.     -- default arguments
  850.     inputOffset = inputOffset or 1
  851.     output = output or {}
  852.     outputOffset = outputOffset or 1
  853.     local state, tmpState = {}, {}
  854.     if (key[KEY_TYPE] ~= DECRYPTION_KEY) then
  855.       error("No decryption key: " .. tostring(key[KEY_TYPE]))
  856.       return
  857.     end
  858.     state = util.bytesToInts(input, inputOffset, 4)
  859.     addRoundKey(state, key, key[ROUNDS])
  860.     local round = key[ROUNDS] - 1
  861.     while (round > 2) do
  862.       -- do a double round to save temporary assignments
  863.       doInvRound(state, tmpState)
  864.       addRoundKey(tmpState, key, round)
  865.       round = round - 1
  866.       doInvRound(tmpState, state)
  867.       addRoundKey(state, key, round)
  868.       round = round - 1
  869.     end
  870.     doInvRound(state, tmpState)
  871.     addRoundKey(tmpState, key, round)
  872.     round = round - 1
  873.     doInvLastRound(tmpState, state)
  874.     addRoundKey(state, key, round)
  875.     util.sleepCheckIn()
  876.     return util.intsToBytes(state, output, outputOffset)
  877.   end
  878.  
  879.   -- calculate all tables when loading this file
  880.   calcSBox()
  881.   calcRoundTables()
  882.   calcInvRoundTables()
  883.  
  884.   return {
  885.     ROUNDS = ROUNDS,
  886.     KEY_TYPE = KEY_TYPE,
  887.     ENCRYPTION_KEY = ENCRYPTION_KEY,
  888.     DECRYPTION_KEY = DECRYPTION_KEY,
  889.     expandEncryptionKey = expandEncryptionKey,
  890.     expandDecryptionKey = expandDecryptionKey,
  891.     encrypt = encrypt,
  892.     decrypt = decrypt,
  893.   }
  894.   end)
  895.  
  896.   local buffer=_W(function(_ENV, ...)
  897.   local function new()
  898.     return {}
  899.   end
  900.  
  901.   local function addString(stack, s)
  902.     table.insert(stack, s)
  903.   end
  904.  
  905.   local function toString(stack)
  906.     return table.concat(stack)
  907.   end
  908.  
  909.   return {
  910.     new = new,
  911.     addString = addString,
  912.     toString = toString,
  913.   }
  914.   end)
  915.  
  916.   ciphermode=_W(function(_ENV, ...)
  917.   local public = {}
  918.   --
  919.   -- Encrypt strings
  920.   -- key - byte array with key
  921.   -- string - string to encrypt
  922.   -- modefunction - function for cipher mode to use
  923.   --
  924.   local random, unpack = math.random, unpack or table.unpack
  925.   function public.encryptString(key, data, modeFunction, iv)
  926.     if iv then
  927.       local ivCopy = {}
  928.       for i = 1, 16 do ivCopy[i] = iv[i] end
  929.       iv = ivCopy
  930.     else
  931.       iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  932.     end
  933.     local keySched = aes.expandEncryptionKey(key)
  934.     local encryptedData = buffer.new()
  935.     for i = 1, #data/16 do
  936.       local offset = (i-1)*16 + 1
  937.       local byteData = {string.byte(data,offset,offset +15)}
  938.       iv = modeFunction(keySched, byteData, iv)
  939.       buffer.addString(encryptedData, string.char(unpack(byteData)))
  940.     end
  941.     return buffer.toString(encryptedData)
  942.   end
  943.   --
  944.   -- the following 4 functions can be used as
  945.   -- modefunction for encryptString
  946.   --
  947.   -- Electronic code book mode encrypt function
  948.   function public.encryptECB(keySched, byteData, iv)
  949.     aes.encrypt(keySched, byteData, 1, byteData, 1)
  950.   end
  951.  
  952.   -- Cipher block chaining mode encrypt function
  953.   function public.encryptCBC(keySched, byteData, iv)
  954.     util.xorIV(byteData, iv)
  955.     aes.encrypt(keySched, byteData, 1, byteData, 1)
  956.     return byteData
  957.   end
  958.  
  959.   -- Output feedback mode encrypt function
  960.   function public.encryptOFB(keySched, byteData, iv)
  961.     aes.encrypt(keySched, iv, 1, iv, 1)
  962.     util.xorIV(byteData, iv)
  963.     return iv
  964.   end
  965.  
  966.   -- Cipher feedback mode encrypt function
  967.   function public.encryptCFB(keySched, byteData, iv)
  968.     aes.encrypt(keySched, iv, 1, iv, 1)
  969.     util.xorIV(byteData, iv)
  970.     return byteData
  971.   end
  972.  
  973.   function public.encryptCTR(keySched, byteData, iv)
  974.     local nextIV = {}
  975.     for j = 1, 16 do nextIV[j] = iv[j] end
  976.     aes.encrypt(keySched, iv, 1, iv, 1)
  977.     util.xorIV(byteData, iv)
  978.     util.increment(nextIV)
  979.     return nextIV
  980.   end
  981.   --
  982.   -- Decrypt strings
  983.   -- key - byte array with key
  984.   -- string - string to decrypt
  985.   -- modefunction - function for cipher mode to use
  986.   --
  987.   function public.decryptString(key, data, modeFunction, iv)
  988.     if iv then
  989.       local ivCopy = {}
  990.       for i = 1, 16 do ivCopy[i] = iv[i] end
  991.       iv = ivCopy
  992.     else
  993.       iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  994.     end
  995.     local keySched
  996.     if modeFunction == public.decryptOFB or modeFunction == public.decryptCFB or modeFunction == public.decryptCTR then
  997.       keySched = aes.expandEncryptionKey(key)
  998.     else
  999.       keySched = aes.expandDecryptionKey(key)
  1000.     end
  1001.     local decryptedData = buffer.new()
  1002.     for i = 1, #data/16 do
  1003.       local offset = (i-1)*16 + 1
  1004.       local byteData = {string.byte(data,offset,offset +15)}
  1005.       iv = modeFunction(keySched, byteData, iv)
  1006.       buffer.addString(decryptedData, string.char(unpack(byteData)))
  1007.     end
  1008.     return buffer.toString(decryptedData)
  1009.   end
  1010.   --
  1011.   -- the following 4 functions can be used as
  1012.   -- modefunction for decryptString
  1013.   --
  1014.   -- Electronic code book mode decrypt function
  1015.   function public.decryptECB(keySched, byteData, iv)
  1016.     aes.decrypt(keySched, byteData, 1, byteData, 1)
  1017.     return iv
  1018.   end
  1019.  
  1020.   -- Cipher block chaining mode decrypt function
  1021.   function public.decryptCBC(keySched, byteData, iv)
  1022.     local nextIV = {}
  1023.     for j = 1, 16 do nextIV[j] = byteData[j] end
  1024.     aes.decrypt(keySched, byteData, 1, byteData, 1)
  1025.     util.xorIV(byteData, iv)
  1026.     return nextIV
  1027.   end
  1028.  
  1029.   -- Output feedback mode decrypt function
  1030.   function public.decryptOFB(keySched, byteData, iv)
  1031.     aes.encrypt(keySched, iv, 1, iv, 1)
  1032.     util.xorIV(byteData, iv)
  1033.     return iv
  1034.   end
  1035.  
  1036.   -- Cipher feedback mode decrypt function
  1037.   function public.decryptCFB(keySched, byteData, iv)
  1038.     local nextIV = {}
  1039.     for j = 1, 16 do nextIV[j] = byteData[j] end
  1040.     aes.encrypt(keySched, iv, 1, iv, 1)
  1041.     util.xorIV(byteData, iv)
  1042.     return nextIV
  1043.   end
  1044.  
  1045.   public.decryptCTR = public.encryptCTR
  1046.   return public
  1047.   end)
  1048.  
  1049.   -- Simple API for encrypting strings.
  1050.   --
  1051.   AES128 = 16
  1052.   AES192 = 24
  1053.   AES256 = 32
  1054.   ECBMODE = 1
  1055.   CBCMODE = 2
  1056.   OFBMODE = 3
  1057.   CFBMODE = 4
  1058.   CTRMODE = 4
  1059.  
  1060.   local function pwToKey(password, keyLength, iv)
  1061.     local padLength = keyLength
  1062.     if (keyLength == AES192) then
  1063.       padLength = 32
  1064.     end
  1065.     if (padLength > #password) then
  1066.       local postfix = ""
  1067.       for i = 1,padLength - #password do
  1068.         postfix = postfix .. string.char(0)
  1069.       end
  1070.       password = password .. postfix
  1071.     else
  1072.       password = string.sub(password, 1, padLength)
  1073.     end
  1074.     local pwBytes = {string.byte(password,1,#password)}
  1075.     password = ciphermode.encryptString(pwBytes, password, ciphermode.encryptCBC, iv)
  1076.     password = string.sub(password, 1, keyLength)
  1077.     return {string.byte(password,1,#password)}
  1078.   end
  1079.   --
  1080.   -- Encrypts string data with password password.
  1081.   -- password  - the encryption key is generated from this string
  1082.   -- data      - string to encrypt (must not be too large)
  1083.   -- keyLength - length of aes key: 128(default), 192 or 256 Bit
  1084.   -- mode      - mode of encryption: ecb, cbc(default), ofb, cfb
  1085.   --
  1086.   -- mode and keyLength must be the same for encryption and decryption.
  1087.   --
  1088.   function encrypt(password, data, keyLength, mode, iv)
  1089.     assert(password ~= nil, "Empty password.")
  1090.     assert(data ~= nil, "Empty data.")
  1091.     local mode = mode or CBCMODE
  1092.     local keyLength = keyLength or AES128
  1093.     local key = pwToKey(password, keyLength, iv)
  1094.     local paddedData = util.padByteString(data)
  1095.     if mode == ECBMODE then
  1096.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptECB, iv)
  1097.     elseif mode == CBCMODE then
  1098.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptCBC, iv)
  1099.     elseif mode == OFBMODE then
  1100.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptOFB, iv)
  1101.     elseif mode == CFBMODE then
  1102.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptCFB, iv)
  1103.     elseif mode == CTRMODE then
  1104.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptCTR, iv)
  1105.     else
  1106.       error("Unknown mode", 2)
  1107.     end
  1108.   end
  1109.   --
  1110.   -- Decrypts string data with password password.
  1111.   -- password  - the decryption key is generated from this string
  1112.   -- data      - string to encrypt
  1113.   -- keyLength - length of aes key: 128(default), 192 or 256 Bit
  1114.   -- mode      - mode of decryption: ecb, cbc(default), ofb, cfb
  1115.   --
  1116.   -- mode and keyLength must be the same for encryption and decryption.
  1117.   --
  1118.   function decrypt(password, data, keyLength, mode, iv)
  1119.     local mode = mode or CBCMODE
  1120.     local keyLength = keyLength or AES128
  1121.     local key = pwToKey(password, keyLength, iv)
  1122.     local plain
  1123.     if mode == ECBMODE then
  1124.       plain = ciphermode.decryptString(key, data, ciphermode.decryptECB, iv)
  1125.     elseif mode == CBCMODE then
  1126.       plain = ciphermode.decryptString(key, data, ciphermode.decryptCBC, iv)
  1127.     elseif mode == OFBMODE then
  1128.       plain = ciphermode.decryptString(key, data, ciphermode.decryptOFB, iv)
  1129.     elseif mode == CFBMODE then
  1130.       plain = ciphermode.decryptString(key, data, ciphermode.decryptCFB, iv)
  1131.     elseif mode == CTRMODE then
  1132.       plain = ciphermode.decryptString(key, data, ciphermode.decryptCTR, iv)
  1133.     else
  1134.       error("Unknown mode", 2)
  1135.     end
  1136.     result = util.unpadByteString(plain)
  1137.     if (result == nil) then
  1138.       return nil
  1139.     end
  1140.     return result
  1141.   end
  1142. end
  1143.  
  1144. local function getFuelLevel()
  1145.   return (lcGate and math.min(100, math.floor((100 / MAXFUEL_LC) * 100)) or math.min(100, math.floor((gate.energyAvailable() / MAXFUEL_SG) * 100)))
  1146. end
  1147.  
  1148. do
  1149.   local function irisControl(state) --# LanteaCraft & SGCraft
  1150.     if irisStatus ~= "Offline" then
  1151.       if lcGate then
  1152.         if state then
  1153.           pcall(gate.closeIris)
  1154.         else
  1155.           pcall(gate.openIris)
  1156.         end
  1157.         waitingForIris = true --# this could be all wrong...without verification there's no way to know what the iris is actually doing
  1158.       else
  1159.         if state then
  1160.           waitingForIris = gate.closeIris()
  1161.         else
  1162.           waitingForIris = gate.openIris()
  1163.         end
  1164.       end
  1165.     end
  1166.   end
  1167.  
  1168.   local actions = {
  1169.     lockdown = function()
  1170.       secureStatus = "lockdown"
  1171.       irisControl(true)
  1172.       hangUp()
  1173.       updateClientAndScreens(true)
  1174.     end;
  1175.     allclear = function()
  1176.       secureStatus = "allclear"
  1177.       irisControl(false)
  1178.       updateClientAndScreens(true)
  1179.     end;
  1180.     QRY = function()
  1181.       updatePercentages()
  1182.     end;
  1183.     dialPause = function(_lcGate) --# LanteaCraft
  1184.       if _lcGate then
  1185.         if gateStatus == "Dialing" then
  1186.           continueDialing = false
  1187.           gateStatus = "Paused"
  1188.         elseif gateStatus == "Paused" then
  1189.           continueDialing = true
  1190.           if lockChevron(chevronNumber + 1) then
  1191.             gateStatus = "Dialing"
  1192.           else
  1193.             continueDialing = false
  1194.           end
  1195.         end
  1196.         displayStatus("pause")
  1197.         updateClientAndScreens()
  1198.       end
  1199.     end;
  1200.     endCall = function()
  1201.       hangUp()
  1202.     end;
  1203.     iCLOSE = function()
  1204.       irisControl(true)
  1205.     end;
  1206.     iOPEN = function(_, _secureStatus)
  1207.       if _secureStatus == "allclear" then irisControl(false) end
  1208.     end;
  1209.   }
  1210.  
  1211.   netReceive = function()
  1212.     local id, message, encKey, encryptedMessage, decryptedMessage, encodedMessage, success, dataPack, thisCommand, tcLen, dialGate
  1213.     while true do
  1214.       if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
  1215.       id, encodedMessage = rednet.receive("ccDHD")
  1216.       if id == gateSettings.DHD and type(encodedMessage) == "string" then
  1217.         success, encryptedMessage = pcall(decode, encodedMessage)
  1218.         if success then
  1219.           encKey = thisCC .. "ccDHD!General_Comms*Key" .. tostring(id)
  1220.           success, decryptedMessage = pcall(decrypt, encKey, encryptedMessage)
  1221.           if success then
  1222.             success, message = pcall(textutils.unserialize, decryptedMessage)
  1223.             if success and type(message) == "table" and message.program then
  1224.               if message.program == "ccDHD" and message.command then
  1225.                 thisCommand = message.command
  1226.                 if actions[thisCommand] then
  1227.                   actions[thisCommand](lcGate, secureStatus)
  1228.                 else
  1229.                   tcLen = #thisCommand
  1230.                   if (tcLen == 7 or tcLen == 9) and gateStatus == "Idle" and secureStatus == "allclear" then
  1231.                     dialGate = false
  1232.                     dialAddress = thisCommand
  1233.                     if lcGate then
  1234.                       continueDialing = true
  1235.                       dialGate = lockChevron(1)
  1236.                       if dialGate then
  1237.                         gateStatus = "Dialing"
  1238.                         callDirection = "Outgoing"
  1239.                         displayStatus("dial")
  1240.                       else
  1241.                         hangUp()
  1242.                       end
  1243.                     else
  1244.                       dialGate = gate.dial(dialAddress)
  1245.                     end
  1246.                     if not dialGate then dialAddress = "none" end
  1247.                     updatePercentages()
  1248.                   end
  1249.                 end
  1250.               elseif message.program == "ccDialer" and message.password and gateStatus == "Connected" then
  1251.                 dataPack = textutils.serialize({ password = message.password })
  1252.                 pcall(gate.sendMessage, dataPack)
  1253.               end
  1254.             end
  1255.           end
  1256.         end
  1257.       end
  1258.     end
  1259.   end
  1260. end
  1261.  
  1262. netSend = function(protocol)
  1263.   local dataPack = textutils.serialize({
  1264.     program = "ccDHD";
  1265.     thisGate = thisGate;
  1266.     fuelPercent = fuelPercent;
  1267.     gateStatus = gateStatus;
  1268.     secureStatus = secureStatus;
  1269.     callDirection = callDirection;
  1270.     dialAddress = dialAddress;
  1271.     incomingAddress = incomingAddress;
  1272.     chevronNumber = chevronNumber;
  1273.     sgRemoteAddrLen = sgRemoteAddrLen;
  1274.     irisState = irisState;
  1275.     irisStatus = irisStatus;
  1276.     irisPercent = irisPercent;
  1277.     waitingForIris = waitingForIris;
  1278.     lcGate = lcGate;
  1279.   })
  1280.   if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
  1281.   if gateSettings.DHD ~= 99999999 then
  1282.     local encKey = tostring(gateSettings.DHD) .. "ccDHD!General_Comms*Key" .. thisCC
  1283.     local encryptedDataPack = encode(encrypt(encKey, dataPack))
  1284.     rednet.send(gateSettings.DHD, encryptedDataPack, protocol or "ccDHD")
  1285.   end
  1286. end
  1287.  
  1288. local function quickSend(dataPack)
  1289.   if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
  1290.   if gateSettings.DHD ~= 99999999 then
  1291.     local encKey = tostring(gateSettings.DHD) .. "ccDHD!General_Comms*Key" .. thisCC
  1292.     local encryptedDataPack = encode(encrypt(encKey, textutils.serialize({ program = "ccDialer", data = dataPack })))
  1293.     rednet.send(gateSettings.DHD, encryptedDataPack, "ccDHD")
  1294.   end
  1295. end
  1296.  
  1297. local function gateReceive() --# LanteaCraft & SGCraft
  1298.   local _, src, message, success, data
  1299.   while true do
  1300.     if lcGate then
  1301.       _, message = os.pullEvent("receive")
  1302.     else
  1303.       _, src, message = os.pullEvent("sgMessageReceived")
  1304.     end
  1305.     success, data = pcall(textutils.unserialize, message)
  1306.     if success and type(data) == "table" then
  1307.       if data.password or data.irisState then
  1308.         quickSend(data.password and data.password or data.irisState)
  1309.       end
  1310.     end
  1311.   end
  1312. end
  1313.  
  1314. do
  1315.   local chevrons7LC = {
  1316.     [1] = { x = 22, y = 5 };  --# 1
  1317.     [2] = { x = 25, y = 8 };  --# 2
  1318.     [3] = { x = 25, y = 13 }; --# 3
  1319.     [4] = { x = 5, y = 13 };  --# 6
  1320.     [5] = { x = 5, y = 8 };   --# 7
  1321.     [6] = { x = 8, y = 5 };   --# 8
  1322.     [7] = { x = 15, y = 3 };  --# 0
  1323.   }
  1324.  
  1325.   local chevrons9LC = {
  1326.     [1] = { x = 22, y = 5 };  --# 1
  1327.     [2] = { x = 25, y = 8 };  --# 2
  1328.     [3] = { x = 25, y = 13 }; --# 3
  1329.     [4] = { x = 5, y = 13 };  --# 6
  1330.     [5] = { x = 5, y = 8 };   --# 7
  1331.     [6] = { x = 8, y = 5 };   --# 8
  1332.     [7] = { x = 22, y = 16 }; --# 4
  1333.     [8] = { x = 8, y = 16 };  --# 5
  1334.     [9] = { x = 15, y = 3 };  --# 0
  1335.   }
  1336.  
  1337.   local chevrons7SG = {
  1338.     [1] = { x = 22, y = 5 };  --# 1
  1339.     [2] = { x = 25, y = 8 };  --# 2
  1340.     [3] = { x = 25, y = 13 }; --# 3
  1341.     [4] = { x = 5, y = 13 };  --# 6
  1342.     [5] = { x = 5, y = 8 };   --# 7
  1343.     [6] = { x = 8, y = 5 };   --# 8
  1344.     [7] = { x = 15, y = 3 };  --# 0
  1345.   }
  1346.  
  1347.   local chevrons9SG = {
  1348.     [1] = { x = 22, y = 5 };  --# 1
  1349.     [2] = { x = 25, y = 8 };  --# 2
  1350.     [3] = { x = 25, y = 13 }; --# 3
  1351.     [4] = { x = 5, y = 13 };  --# 6
  1352.     [5] = { x = 5, y = 8 };   --# 7
  1353.     [6] = { x = 8, y = 5 };   --# 8
  1354.     [7] = { x = 22, y = 16 }; --# 4
  1355.     [8] = { x = 8, y = 16 };  --# 5
  1356.     [9] = { x = 15, y = 3 };  --# 0
  1357.   }
  1358.  
  1359.   local wormhole = {
  1360.     [1] = { x = 11, y = 5, line = "         " };
  1361.     [2] = { x = 10, y = 6, line = "           " };
  1362.     [3] = { x = 9, y = 7, line = "             " };
  1363.     [4] = { x = 8, y = 8, line = "               " };
  1364.     [5] = { x = 7, y = 9, line = "                 " };
  1365.     [6] = { x = 6, y = 10, line = "                   " };
  1366.     [7] = { x = 6, y = 11, line = "                   " };
  1367.     [8] = { x = 7, y = 12, line = "                 " };
  1368.     [9] = { x = 8, y = 13, line = "               " };
  1369.     [10] = { x = 9, y = 14, line = "             " };
  1370.     [11] = { x = 10, y = 15, line = "           " };
  1371.     [12] = { x = 11, y = 16, line = "         " };
  1372.   }
  1373.  
  1374.   local function displayWormhole()
  1375.     local bgColor = gateStatus == "Connected" and mblue or mblack
  1376.     for i = 1, hardware.gateMon do
  1377.       gateMon[i].setBackgroundColor(bgColor)
  1378.       for j = 1, 12 do
  1379.         gateMon[i].setCursorPos(wormhole[j].x, wormhole[j].y)
  1380.         gateMon[i].write(wormhole[j].line)
  1381.       end
  1382.     end
  1383.   end
  1384.  
  1385.   local function displayGlyph(i, chevron, addrLen, backSpace)
  1386.     if addrLen == 7 or addrLen == 9 or backSpace then
  1387.       if lcGate then
  1388.         gateMon[i].setCursorPos((addrLen == 7) and chevrons7LC[chevron].x or chevrons9LC[chevron].x, (addrLen == 7) and chevrons7LC[chevron].y or chevrons9LC[chevron].y)
  1389.       else
  1390.         if chevron > sgRemoteAddrLen then sgRemoteAddrLen = 9 end
  1391.         gateMon[i].setCursorPos((addrLen == 7 or sgRemoteAddrLen == 7) and chevrons7SG[chevron].x or chevrons9SG[chevron].x, (addrLen == 7 or sgRemoteAddrLen == 7) and chevrons7SG[chevron].y or chevrons9SG[chevron].y)
  1392.       end
  1393.       if backSpace then
  1394.         gateMon[i].write(" ")
  1395.       else
  1396.         gateMon[i].write(incomingAddress and incomingAddress:sub(chevron, chevron) or dialAddress:sub(chevron, chevron))
  1397.       end
  1398.     end
  1399.   end
  1400.  
  1401.   displayChevrons = function(init, backSpace)
  1402.     if not gateMon[1] or (bAndW and not grayScale) then return end
  1403.     local gm
  1404.     if gateStatus == "Idle" then
  1405.       if not init then
  1406.         for i = 1, hardware.gateMon do
  1407.           gm = gateMon[i]
  1408.           gm.setBackgroundColor(msilver)
  1409.           for j = 1, 9 do
  1410.             gm.setCursorPos(chevrons9LC[j].x, chevrons9LC[j].y)
  1411.             gm.write(" ")
  1412.           end
  1413.         end
  1414.         displayWormhole()
  1415.       end
  1416.     elseif gateStatus == "Connected" then
  1417.       local addrLen = incomingAddress and #incomingAddress or #dialAddress
  1418.       if init then
  1419.         if addrLen == 7 or addrLen == 9 then
  1420.           for i = 1, hardware.gateMon do
  1421.             gateMon[i].setBackgroundColor(morange)
  1422.             gateMon[i].setTextColor(mblack)
  1423.             for j = 1, addrLen do
  1424.               displayGlyph(i, j, addrLen)
  1425.             end
  1426.           end
  1427.         end
  1428.       else
  1429.         if addrLen == 7 and dialFromDHD then
  1430.           for i = 1, hardware.gateMon do
  1431.             gm = gateMon[i]
  1432.             gm.setBackgroundColor(msilver)
  1433.             for j = 1, 9 do
  1434.               gm.setCursorPos(chevrons9LC[j].x, chevrons9LC[j].y)
  1435.               gm.write(" ")
  1436.             end
  1437.             gm.setBackgroundColor(morange)
  1438.             gm.setTextColor(mblack)
  1439.             for j = 1, addrLen do
  1440.               displayGlyph(i, j, addrLen)
  1441.             end
  1442.           end
  1443.         end
  1444.       end
  1445.       displayWormhole()
  1446.     elseif gateStatus == "Dialing" and backSpace then
  1447.       for i = 1, hardware.gateMon do
  1448.         gateMon[i].setBackgroundColor(msilver)
  1449.         displayGlyph(i, chevronNumber + 1, 9, true)
  1450.       end
  1451.     elseif gateStatus ~= "Disconnecting" then
  1452.       if chevronNumber > 0 then
  1453.         local addrLen = incomingAddress and #incomingAddress or #dialAddress
  1454.         for i = 1, hardware.gateMon do
  1455.           gateMon[i].setBackgroundColor(morange)
  1456.           gateMon[i].setTextColor(mblack)
  1457.           if init then
  1458.             for j = 1, chevronNumber do
  1459.               displayGlyph(i, j, addrLen)
  1460.             end
  1461.           else
  1462.             displayGlyph(i, chevronNumber, addrLen)
  1463.           end
  1464.         end
  1465.       end
  1466.     end
  1467.   end
  1468. end
  1469.  
  1470. do
  1471.   local function makeLongName(name) --# This spaces out a 7 character address
  1472.     return #name ~= 7 and name or table.concat({ name:sub(1, 1), " ", name:sub(2, 2), " ", name:sub(3, 3), " ", name:sub(4, 4), " ", name:sub(5, 5), " ", name:sub(6, 6), " ", name:sub(7) })
  1473.   end
  1474.  
  1475.   displayStatus = function(state)
  1476.     if not marquee[1] then return end
  1477.     if state == "idle" then
  1478.       if secureStatus == "allclear" then --# Display the local address when there is no other information to display
  1479.         local longAddress, tmpX = makeLongName(thisGate), #thisGate - 5
  1480.         for i = 1, hardware.marquee do
  1481.           marquee[i].clear()
  1482.           marquee[i].setCursorPos(1, 1)
  1483.           marquee[i].setTextColor(mcyan)
  1484.           marquee[i].write("Stargate")
  1485.           marquee[i].setTextColor(myellow)
  1486.           marquee[i].setCursorPos(tmpX, 2)
  1487.           marquee[i].write(longAddress)
  1488.         end
  1489.       else
  1490.         for i = 1, hardware.marquee do
  1491.           marquee[i].clear()
  1492.           marquee[i].setCursorPos(1, 1)
  1493.           marquee[i].setTextColor(mred)
  1494.           marquee[i].write("!! LOCKDOWN !!")
  1495.         end
  1496.       end
  1497.     elseif state == "dial" or (state == "con" and not incomingAddress) then
  1498.       local longAddress, tmpX = makeLongName(dialAddress), #dialAddress - 5
  1499.       for i = 1, hardware.marquee do
  1500.         marquee[i].setCursorPos(1, 1)
  1501.         marquee[i].setTextColor(msky)
  1502.         if state == "dial" then
  1503.           marquee[i].write("Dialing     ")
  1504.           marquee[i].setTextColor(mgray)
  1505.         elseif state == "con" then
  1506.           marquee[i].write(dialAddress == "none" and "Connected" or "Connected to")
  1507.           marquee[i].setTextColor(myellow)
  1508.         end
  1509.         if dialAddress ~= "none" then
  1510.           marquee[i].setCursorPos(tmpX, 2)
  1511.           marquee[i].write(longAddress)
  1512.         end
  1513.       end
  1514.     elseif state == "incoming" or (state == "con" and incomingAddress) then
  1515.       local longAddress = makeLongName(incomingAddress)
  1516.       local iaLen = #incomingAddress
  1517.       local sBuffer, iaX = string.rep(" ", 9 - iaLen), iaLen - 5
  1518.       for i = 1, hardware.marquee do
  1519.         marquee[i].setTextColor(secureStatus == "allclear" and msky or mred)
  1520.         marquee[i].setCursorPos(1, 1)
  1521.         marquee[i].write(incomingAddress == "Wormhole" and "Incoming     " or "Incoming from ")
  1522.         marquee[i].setTextColor(secureStatus == "allclear" and myellow or morange)
  1523.         if gateStatus == "Dialing" then
  1524.           if lcGate then
  1525.             marquee[i].setCursorPos(4, 2)
  1526.             marquee[i].write(incomingAddress .. sBuffer)
  1527.           else
  1528.             marquee[i].setCursorPos(iaX, 2)
  1529.             if iaLen == 7 then
  1530.               marquee[i].write(longAddress:sub(1, chevronNumber * 2))
  1531.               marquee[i].setTextColor(mgray)
  1532.               marquee[i].write(longAddress:sub((chevronNumber * 2) + 1))
  1533.             else
  1534.               marquee[i].write(incomingAddress:sub(1, chevronNumber))
  1535.               marquee[i].setTextColor(mgray)
  1536.               marquee[i].write(incomingAddress:sub(chevronNumber + 1))
  1537.             end
  1538.           end
  1539.         else
  1540.           marquee[i].setCursorPos(iaX, 2)
  1541.           marquee[i].write(longAddress)
  1542.         end
  1543.       end
  1544.     elseif state == "outgoing" then
  1545.       local longAddress, daLen = makeLongName(dialAddress), #dialAddress
  1546.       local daX = daLen - 5
  1547.       for i = 1, hardware.marquee do
  1548.         marquee[i].setCursorPos(daX, 2)
  1549.         marquee[i].setTextColor(myellow)
  1550.         if daLen == 7 then
  1551.           marquee[i].write(longAddress:sub(1, chevronNumber * 2))
  1552.         else
  1553.           marquee[i].write(dialAddress:sub(1, chevronNumber))
  1554.         end
  1555.       end
  1556.     elseif state == "pause" then
  1557.       if gateStatus == "Dialing" or gateStatus == "Paused" then
  1558.         for i = 1, hardware.marquee do
  1559.           marquee[i].setCursorPos(1, 1)
  1560.           marquee[i].setTextColor(msky)
  1561.           marquee[i].write(gateStatus == "Dialing" and "Dialing     " or "Paused      ")
  1562.         end
  1563.       end
  1564.     elseif state == "discon" then
  1565.       for i = 1, hardware.marquee do
  1566.         marquee[i].clear()
  1567.         marquee[i].setCursorPos(1, 1)
  1568.         marquee[i].setTextColor(msky)
  1569.         marquee[i].write(gateStatus)
  1570.       end
  1571.     end
  1572.   end
  1573. end
  1574.  
  1575. local function displayConnectionTime()
  1576.   drawElement(30, 4, 9, 1, white, black, connectionClock)
  1577.   if not timerMon[1] then return end
  1578.   for i = 1, hardware.timerMon do
  1579.     if connectionTime < 2 then
  1580.       timerMon[i].setCursorPos(1, 1)
  1581.       timerMon[i].setTextColor(connectionTime > 0 and mgreen or mgray)
  1582.       timerMon[i].write("Connect")
  1583.     end
  1584.     timerMon[i].setCursorPos(1, 2)
  1585.     timerMon[i].setTextColor(connectionTime > 0 and mwhite or mgray)
  1586.     timerMon[i].write(connectionClock)
  1587.   end
  1588. end
  1589.  
  1590. do
  1591.   local termX, termY = term.getSize()
  1592.   local validColors = { }
  1593.   if fullColor then
  1594.     validColors = { [1] = true; [2] = true; [4] = true; [8] = true; [16] = true; [32] = true; [64] = true; [128] = true; [256] = true; [512] = true; [1024] = true; [2048] = true; [4096] = true; [8192] = true; [16384] = true, [32768] = true; }
  1595.   elseif grayScale then
  1596.     validColors = { [1] = true; [128] = true; [256] = true; [32768] = true; }
  1597.   else
  1598.     validColors = { [1] = true; [32768] = true; }
  1599.   end
  1600.  
  1601.   drawElement = function(x, y, w, h, txtColor, bgColor, text)
  1602.     if type(x) == "number" and type(y) == "number" and type(w) == "number" and type(h) == "number" then
  1603.       local sText = text and tostring(text) or "" --# Ensure text is a string
  1604.       w = math.floor(math.min(math.max(1, math.max(#sText, w)), termX)) --# Validate width
  1605.       h = math.floor(math.max(1, math.min(h, termY)))                   --# valid height
  1606.       x = math.floor(math.max(1, math.min(x, termX - w + 1)))           --# validate x coord
  1607.       y = math.floor(math.max(1, math.min(y, termY - h + 1)))           --# validate y coord
  1608.       local spacer = (w - #sText) / 2
  1609.       local txtLine = string.rep(" ", math.floor(spacer)) .. sText .. string.rep(" ", math.ceil(spacer))
  1610.       if type(txtColor) == "number" and validColors[txtColor] then term.setTextColor(txtColor) end    --# validate the text color
  1611.       if type(bgColor) == "number" and validColors[bgColor] then term.setBackgroundColor(bgColor) end --# validate the background color
  1612.       if h == 1 then                   --# if the height is 1 then...
  1613.         term.setCursorPos(x, y)        --# Position cursor
  1614.         term.write(txtLine)            --# Draw the single line of the 'element'
  1615.       else                             --# otherwise...
  1616.         local line, txtRow = string.rep(" ", w), math.floor(h / 2) + y --# Define one line of the 'element' (box/rectangle/line-seg) and the row the text will be drawn on
  1617.         for i = y, y + h - 1 do        --# Loop through the height of the 'element' line by line (top to bottom)
  1618.           term.setCursorPos(x, i)      --# Position cursor
  1619.           term.write(i == txtRow and txtLine or line) --# Draw 1 of h lines of the 'element' (box/rectangle/line-seg)
  1620.         end
  1621.       end
  1622.     end
  1623.   end
  1624. end
  1625.  
  1626. updateClientAndScreens = function(updateMonitors, init)
  1627.   netSend()
  1628.   if updateMonitors then
  1629.     if gateStatus == "Idle" then
  1630.       displayStatus("idle")
  1631.     elseif gateStatus == "Connected" then
  1632.       displayStatus("con")
  1633.     elseif gateStatus == "Dialing" then
  1634.       displayStatus(incomingAddress and "incoming" or (chevronNumber > 0 and "outgoing" or "dial")) --# incoming or outgoing
  1635.     elseif gateStatus == "Paused" then
  1636.       displayStatus("pause")
  1637.     elseif gateStatus == "Disconnecting" then
  1638.       displayStatus("discon")
  1639.     end
  1640.     displayChevrons(init)
  1641.   end
  1642.   --# terminal screen
  1643.   local txtColor, bgColor = fullColor and white or black, fullColor and blue or white --# Set the header color
  1644.   --# Static entries
  1645.   if not staticDrawn then
  1646.     --# Header/Footer
  1647.     local glHeaderTxt = "Gate Liaison            ver. " .. glVer
  1648.     drawElement(2, 2, 37, 1, txtColor, bgColor, glHeaderTxt) --# Header
  1649.     drawElement(2, 10, 37, 1)                                --# Footer
  1650.     drawElement(3, 10, 1, 1, nil, nil, ccLabel)              --# Lowline
  1651.     local ccID = "cc# " .. thisCC
  1652.     drawElement(38 - #ccID, 10, 1, 1, nil, nil, ccID)
  1653.     --# Body
  1654.     drawElement(2, 3, 12, 7, nil, silver)                    --# Labels' background
  1655.     local readouts = { " This Gate:", " State:", " Target:",  " Iris:", " Fuel:", " Status:", " Network:" }
  1656.     for i = 1, 7 do
  1657.       drawElement(2, i + 2, 1, 1, black, nil, readouts[i])
  1658.     end
  1659.     drawElement(17, 9, 1, 1, silver, black, modemSide)
  1660.     drawElement(24, 9, 1, 1, fullColor and (wifiComms and orange or green) or white, nil, wifiComms and "(WiFi)" or "(Wired)")
  1661.     drawElement(32, 9, 1, 1, (fullColor or grayScale) and gray or white, nil, "(" .. tostring(gateSettings.DHD) .. ")")
  1662.     staticDrawn = true
  1663.   end
  1664.   --# Dynamic entries
  1665.   drawElement(17, 3, 1, 1, secureStatus == "allclear" and yellow or red, nil, secureStatus == "allclear" and thisGate .. "     " or "!! LOCKDOWN !!")
  1666.   txtColor = white
  1667.   if gateStatus == "Idle" then
  1668.     txtColor = green
  1669.   elseif gateStatus == "Connected" then
  1670.     txtColor = orange
  1671.   elseif gateStatus == "Dialing" or gateStatus == "Paused" or gateStatus == "Disconnecting" then
  1672.     txtColor = sky
  1673.   end
  1674.   if gateStatus == "Dialing" and dialAddress == "none" and callDirection == "Incoming" then
  1675.     drawElement(17, 4, 1, 1, txtColor, black, incomingAddress and "Incoming    " or "Incoming Wormhole")
  1676.   else
  1677.     drawElement(17, 4, 13, 1, nil, black) --# clear gateStatus line (so lingering characters are removed and the connection timer isn't overwritten by adding string.rep to the output line)
  1678.     drawElement(17, 4, 1, 1, txtColor, nil, gateStatus)
  1679.   end
  1680.   drawElement(30, 4, 9, 1, white, nil, connectionClock) --# connection timer
  1681.   drawElement(17, 5, 1, 1, fullColor and gray or white, nil, incomingAddress and incomingAddress .. string.rep(" ", 9 - #incomingAddress) or dialAddress .. string.rep(" ", 9 - #dialAddress))
  1682.   if (incomingAddress or dialAddress ~= "none") and chevronNumber > 0 and gateStatus ~= "Connected" then
  1683.     drawElement(17, 5, 1, 1, yellow, nil, incomingAddress and incomingAddress:sub(1, chevronNumber) or dialAddress:sub(1, chevronNumber))
  1684.   elseif (incomingAddress or dialAddress ~= "none") and gateStatus == "Connected" then
  1685.     drawElement(17, 5, 1, 1, yellow, nil, incomingAddress or dialAddress)
  1686.   end
  1687.   drawElement(17, 6, 1, 1, irisState and green or orange, nil, irisStatus .. "   ")
  1688.   if lcGate then
  1689.     local irisColor = irisPercent > 49 and green or orange
  1690.     irisColor = irisPercent < 25 and red or irisColor
  1691.     drawElement(25, 6, 1, 1, irisColor, nil, tostring(irisPercent))
  1692.   end
  1693.   term.setTextColor(silver)
  1694.   term.write(waitingForIris and (lcGate and "%  (waiting)  " or " (waiting)    ") or (lcGate and "%             " or "              "))
  1695.   local fuelColor = fuelPercent > 24 and green or orange
  1696.   fuelColor = fuelPercent < 6 and red or fuelColor
  1697.   drawElement(17, 7, 1, 1, fuelColor, nil, tostring(fuelPercent))
  1698.   term.setTextColor(silver)
  1699.   term.write("%  ")
  1700.   drawElement(17, 8, 1, 1, secureStatus == "allclear" and green or red, nil, secureStatus)
  1701. end
  1702.  
  1703. updatePercentages = function(updateMonitors, init)
  1704.   if updateTimer then os.cancelTimer(updateTimer) updateTimer = os.startTimer(5) end
  1705.   fuelPercent = getFuelLevel()
  1706.   if lcGate and irisStatus ~= "Offline" then irisPercent = gate.getIrisHealth() end
  1707.   updateClientAndScreens(updateMonitors, init)
  1708. end
  1709.  
  1710. local function recordSessionData() --# Human readable log files (last call)
  1711.   local logAddress = incomingAddress or dialAddress
  1712.   logAddress = logAddress == "Wormhole" and "N/A" or logAddress
  1713.   local callTime = textutils.formatTime(os.time(), false)
  1714.   local lastCall = fs.open("/data/gateLastCall", "w")
  1715.   lastCall.writeLine(tostring(os.day()) .. " @ " .. callTime .. " <" .. callDirection .. "> " .. logAddress)
  1716.   lastCall.close()
  1717. end
  1718.  
  1719. local function disengageChevron(auto) --# LanteaCraft
  1720.   if auto then autoDisengage = true end
  1721.   --local onHook = pcall(gate.deactivateChevron)
  1722.   --if onHook then gateStatus = "Disconnecting" end
  1723.   pcall(gate.deactivateChevron)
  1724.   if secureStatus == "allclear" then gateStatus = "Disconnecting" end
  1725. end
  1726.  
  1727. hangUp = function() --# LanteaCraft & SGCraft
  1728.   if gateStatus == "Idle" or gateStatus == "Disconnecting" then return end
  1729.   local onHook = false
  1730.   if lcGate then
  1731.     if gateStatus == "Connected" then
  1732.       --onHook = pcall(gate.disengageStargate)
  1733.       --if onHook then gateStatus = "Disconnecting" displayStatus("discon") end
  1734.       pcall(gate.disengageStargate)
  1735.       gateStatus = "Disconnecting" --# this might be a problem - if gate doesn't disconnect, hangUp won't be processed again due to gate status
  1736.       displayStatus("discon")
  1737.       if gate.getActivatedChevrons() > 0 then disengageChevron(true) end
  1738.     elseif gateStatus == "Dialing" or gateStatus == "Paused" then --# outgoing calls only?
  1739.       continueDialing = false
  1740.       while spinTime do
  1741.         os.queueEvent("spinWait")
  1742.         os.pullEvent("spinWait")
  1743.       end
  1744.       disengageChevron(true)
  1745.       if gateStatus == "Disconnecting" then displayStatus("discon") end
  1746.     end
  1747.   else
  1748.     onHook = gate.disconnect()
  1749.     if onHook then
  1750.       gateStatus = "Disconnecting"
  1751.       displayStatus("discon")
  1752.     end
  1753.   end
  1754.   updateClientAndScreens()
  1755. end
  1756.  
  1757. lockChevron = function(num) --# LanteaCraft
  1758.   --local dialGate = false
  1759.   if continueDialing then
  1760.     --dialGate = pcall(gate.selectGlyph, dialAddress:sub(num, num))
  1761.     --if not dialGate then return false end
  1762.     --dialGate = pcall(gate.activateChevron)
  1763.     pcall(gate.selectGlyph, dialAddress:sub(num, num))
  1764.     pcall(gate.activateChevron)
  1765.   end
  1766.   --return dialGate
  1767.   return true
  1768. end
  1769.  
  1770. local function lcIrisMonitor() --# LanteaCraft
  1771.   local event
  1772.   while true do
  1773.     event = os.pullEvent()
  1774.     if event == "irisOpened" or event == "irisOpening" or event == "irisDestroyed" or event == "irisClosed" or event == "irisClosing" then
  1775.       irisState = (event == "irisClosed" or event == "irisOpening")
  1776.       irisStatus = event ~= "irisOpened" and event:sub(5) or "Open"
  1777.       if irisStatus == "Destroyed" then irisStatus = "Offline" end
  1778.       waitingForIris = (irisStatus == "Opening" or irisStatus == "Closing")
  1779.       --irisPercent = gate.getIrisHealth()
  1780.       updateClientAndScreens()
  1781.       --[[
  1782.       if gateStatus == "Connected" then
  1783.         local iris = irisState and "closed" or "open"
  1784.         local dataPack = textutils.serialize({ irisState = iris })
  1785.         pcall(gate.sendMessage, dataPack)
  1786.       end
  1787.       ]]--
  1788.     end
  1789.   end
  1790. end
  1791.  
  1792. local function lcGateMonitor() --# LanteaCraft
  1793.   local event
  1794.   while true do
  1795.     event = os.pullEvent()
  1796.     if event == "spinToGlyph" then
  1797.       spinTime = true
  1798.       if rsSide ~= "none" and outgoingAlarm then rs.setOutput(rsSide, true) end
  1799.     elseif event == "disengageGlyph" then
  1800.       chevronNumber = chevronNumber - 1
  1801.       if secureStatus == "allclear" then
  1802.         if chevronNumber == 0 then
  1803.           if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  1804.           gateStatus = "Idle"
  1805.           gateStatus, autoDisengage, dialFromDHD = "Idle", false, false
  1806.           dialAddress, callDirection = "none", "none"
  1807.           incomingAddress = nil
  1808.         else
  1809.           if dialFromDHD then
  1810.             dialAddress = dialAddress:sub(1, chevronNumber) .. string.rep("?", 9 - chevronNumber)
  1811.           else
  1812.             dialAddress = dialAddress:sub(1, chevronNumber)
  1813.           end
  1814.           displayChevrons(nil, true)
  1815.           if autoDisengage then disengageChevron() end
  1816.         end
  1817.         updateClientAndScreens(true)
  1818.       else
  1819.         if chevronNumber > 0 then
  1820.           disengageChevron()
  1821.         end
  1822.       end
  1823.     elseif event == "connect" then
  1824.       gateStatus = "Connected"
  1825.       spinTime = false
  1826.       if dialAddress:find("?") then dialAddress = gate.getActivatedGlyphs() end
  1827.       if dialAddress == "none" then incomingAddress = "Wormhole" end
  1828.       if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  1829.       displayStatus("con")
  1830.       quickSend("Connected")
  1831.       sleep(0.02)
  1832.       updatePercentages(true)
  1833.       --local iris = irisState and "closed" or "open"
  1834.       --local dataPack = textutils.serialize({ irisState = iris })
  1835.       --pcall(gate.sendMessage, dataPack)
  1836.       recordSessionData()
  1837.       if callDirection == "Outgoing" then updateTimer = os.startTimer(5) end
  1838.       connectionTimer = os.startTimer(0.1)
  1839.     elseif event == "disconnect" then
  1840.       gateStatus, autoDisengage = "Idle", false
  1841.       dialAddress, callDirection = "none", "none"
  1842.       dialFromDHD, gateDiscon, incomingAddress = false, false, nil
  1843.       chevronNumber = 0
  1844.       if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  1845.       quickSend("Idle")
  1846.       sleep(0.02)
  1847.       if connectionTimer then os.cancelTimer(connectionTimer) connectionTimer = nil end
  1848.       connectionTime, connectionClock = 0, "00:00:0"
  1849.       updateClientAndScreens(true)
  1850.       displayConnectionTime()
  1851.     end
  1852.   end
  1853. end
  1854.  
  1855. local function sgIrisMonitor() --# SGCraft
  1856.   local event, _, oldState, iris
  1857.   while true do
  1858.     event, _, irisStatus, oldState = os.pullEvent("sgIrisStateChange")
  1859.     waitingForIris = (irisStatus == "Opening" or irisStatus == "Closing")
  1860.     if not waitingForIris then
  1861.       irisState = irisStatus == "Closed"
  1862.     end
  1863.     updateClientAndScreens()
  1864.     if gateStatus == "Connected" then
  1865.       iris = irisState and "closed" or "open"
  1866.       gate.sendMessage(textutils.serialize({ irisState = iris }))
  1867.     end
  1868.   end
  1869. end
  1870.  
  1871. local function sgGateMonitor() --# SGCraft
  1872.   local sgEvent, _, newState, oldState, iris
  1873.   while true do
  1874.     sgEvent, _, newState, oldState = os.pullEvent("sgStargateStateChange")
  1875.     gateStatus = sgStates[newState] or "Unknown"
  1876.     if gateStatus == "Idle" or gateStatus == "Offline" then
  1877.       callDirection, dialAddress = "none", "none"
  1878.       dialFromDHD, gateDiscon, incomingAddress = false, false, nil
  1879.       chevronNumber, sgRemoteAddrLen = 0, 0
  1880.       if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  1881.       quickSend("Idle")
  1882.       sleep(0.02)
  1883.       if connectionTimer then os.cancelTimer(connectionTimer) connectionTimer = nil end
  1884.       connectionTime, connectionClock = 0, "00:00:0"
  1885.       displayConnectionTime()
  1886.     elseif gateStatus == "Connected" then
  1887.       if dialAddress:find("?") then
  1888.         dialAddress = dialAddress:sub(1, dialAddress:find("?") - 1)
  1889.       end
  1890.       if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  1891.       displayStatus("con")
  1892.       updatePercentages(true)
  1893.       iris = irisState and "closed" or "open"
  1894.       gate.sendMessage(textutils.serialize({ irisState = iris }))
  1895.       quickSend("Connected")
  1896.       recordSessionData()
  1897.       if callDirection == "Outgoing" then updateTimer = os.startTimer(5) end
  1898.       connectionTimer = os.startTimer(0.1)
  1899.       if secureStatus == "lockdown" and callDirection == "Outgoing" then hangUp() end
  1900.     --elseif gateStatus == "Dialing" then
  1901.       --if rsSide ~= "none" then rs.setOutput(rsSide, true) end
  1902.     end
  1903.     if gateStatus ~= "Dialing" and gateStatus ~= "Connected" and gateStatus ~= "Unknown" then
  1904.       updateClientAndScreens(true)
  1905.     end
  1906.   end
  1907. end
  1908.  
  1909. local function lcChevronEncoder() --# LanteaCraft
  1910.   --local establishWormhole
  1911.   local daLen
  1912.   while true do
  1913.     os.pullEvent("engageGlyph")
  1914.     spinTime = false
  1915.     chevronNumber = chevronNumber + 1
  1916.     if secureStatus == "allclear" then
  1917.       gateStatus = "Dialing"
  1918.       if dialAddress:find("?") or (dialAddress == "none" and not incomingAddress) then --# watch for standard DHD activity
  1919.         dialAddress = gate.getActivatedGlyphs() .. string.rep("?", 9 - gate.getActivatedChevrons())
  1920.         dialFromDHD = true
  1921.       end
  1922.       if incomingAddress then
  1923.         callDirection = "Incoming"
  1924.       else
  1925.         callDirection = "Outgoing"
  1926.         --if updateTimer then os.cancel(updateTimer) end
  1927.         --updateTimer = os.startTimer(5)
  1928.         if dialAddress:find("?") and chevronNumber == 1 then
  1929.           displayStatus("dial")
  1930.         end
  1931.         --if gateStatus == "Dialing" and callDirection == "none" then callDirection = "Unknown" end
  1932.         daLen = #dialAddress
  1933.         if chevronNumber == daLen then    --# all chevrons engaged, time to establish the wormhole
  1934.           continueDialing = false
  1935.           --establishWormhole = pcall(gate.engageStargate)
  1936.           --if not establishWormhole then hangUp() end
  1937.           pcall(gate.engageStargate)
  1938.         elseif chevronNumber < daLen then --# engage the next chevron
  1939.           if continueDialing and not lockChevron(chevronNumber + 1) then
  1940.             continueDialing = false
  1941.             gateStatus = "Paused"
  1942.           end
  1943.         end
  1944.       end
  1945.       quickSend(gateStatus == "Dialing" and "Dialing" or "Paused")
  1946.       sleep(0.02)
  1947.       updateClientAndScreens(true)
  1948.     else
  1949.       disengageChevron()
  1950.     end
  1951.   end
  1952. end
  1953.  
  1954. local function sgChevronUnencoded() --# SGCraft
  1955.   local sgEvent, _
  1956.   while true do
  1957.     sgEvent, _, chevronNumber = os.pullEvent("sgChevronUnset")
  1958.     if gateStatus == "Dialing" then
  1959.       if chevronNumber == 0 then
  1960.         dialAddress, callDirection, gateStatus, dialFromDHD, gateDiscon = "none", "none", "Idle", false, false
  1961.         if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  1962.       else
  1963.         dialAddress = dialAddress:sub(1, chevronNumber) .. string.rep("?", 9 - chevronNumber)
  1964.       end
  1965.       updateClientAndScreens()
  1966.       displayChevrons(nil, true)
  1967.     end
  1968.   end
  1969. end
  1970.  
  1971. local function sgChevronEncoder() --# SGCraft
  1972.   local sgEvent, _, chevron
  1973.   while true do
  1974.     sgEvent, _, chevronNumber, chevron = os.pullEvent("sgChevronEngaged")
  1975.     gateStatus = "Dialing"
  1976.     if incomingAddress then
  1977.       displayStatus("incoming")
  1978.     else
  1979.       if dialAddress:find("?") then
  1980.         dialAddress = dialAddress:sub(1, chevronNumber - 1) .. chevron .. string.rep("?", 9 - chevronNumber)
  1981.         if disconTimer then os.cancelTimer(disconTimer) end
  1982.         disconTimer = os.startTimer(3)
  1983.       elseif dialAddress == "none" then --# watch for standard DHD activity
  1984.         dialAddress = chevron .. "????????"
  1985.         callDirection = "Outgoing"
  1986.         dialFromDHD = true
  1987.         displayStatus("dial")
  1988.         if rsSide ~= "none" then rs.setOutput(rsSide, true) end
  1989.         if disconTimer then os.cancelTimer(disconTimer) end
  1990.         disconTimer = os.startTimer(3)
  1991.       end
  1992.       displayStatus("outgoing")
  1993.     end
  1994.     updateClientAndScreens()
  1995.     displayChevrons()
  1996.   end
  1997. end
  1998.  
  1999. local function outgoingCall()   --# SGCraft
  2000.   local _, sgEvent, outgoingAddress
  2001.   while true do
  2002.     sgEvent, _, outgoingAddress = os.pullEvent("sgDialOut")
  2003.     sgRemoteAddrLen = #outgoingAddress
  2004.     if dialAddress == "none" then dialAddress = outgoingAddress end
  2005.     gateStatus = "Dialing"
  2006.     callDirection = "Outgoing"
  2007.     if rsSide ~= "none" and outgoingAlarm then rs.setOutput(rsSide, true) end
  2008.     quickSend("Dialing")
  2009.     sleep(0.02)
  2010.     updateClientAndScreens(true)
  2011.   end
  2012. end
  2013.  
  2014. local function incomingCall()   --# SGCraft
  2015.   local _, sgEvent
  2016.   while true do
  2017.     sgEvent, _, incomingAddress = os.pullEvent("sgDialIn")
  2018.     sgRemoteAddrLen = #incomingAddress     --# get the 'native' address length
  2019.     incomingAddress = gate.remoteAddress() --# get the full, 9 symbol address if available
  2020.     gateStatus = "Dialing"
  2021.     callDirection = "Incoming"
  2022.     if rsSide ~= "none" then rs.setOutput(rsSide, true) end
  2023.     quickSend("Dialing")
  2024.     sleep(0.02)
  2025.     updateClientAndScreens(true)
  2026.   end
  2027. end
  2028.  
  2029. local function charInput()
  2030.   local _, char
  2031.   while true do
  2032.     _, char = os.pullEvent("char")
  2033.     if string.lower(char) == "q" then
  2034.       if rednet.isOpen(modemSide) then rednet.close(modemSide) end
  2035.       if marquee[1] then
  2036.         for i = 1, hardware.marquee do
  2037.           marquee[i].clear()
  2038.         end
  2039.       end
  2040.       if gateMon[1] then
  2041.         for i = 1, hardware.gateMon do
  2042.           gateMon[i].setBackgroundColor(mblack)
  2043.           gateMon[i].clear()
  2044.         end
  2045.       end
  2046.       if timerMon[1] then
  2047.         for i = 1, hardware.timerMon do
  2048.           timerMon[i].clear()
  2049.         end
  2050.       end
  2051.       term.setBackgroundColor(black)
  2052.       term.clear()
  2053.       drawElement(1, 1, 1, 1, white, nil, "gateLiaison is OFFLINE")
  2054.       term.setCursorPos(1, 3)
  2055.       return
  2056.     end
  2057.   end
  2058. end
  2059.  
  2060. local function dataPoller()
  2061.   local _, timer, newFuel, newIris, update, monUpdate, hours, minutes, seconds, sMinutes, sSeconds, sTenths
  2062.   while true do
  2063.     _, timer = os.pullEvent("timer")
  2064.     update, monUpdate = false, false
  2065.     if timer == updateTimer then
  2066.       newFuel = getFuelLevel()
  2067.       update = newFuel ~= fuelPercent
  2068.       fuelPercent = newFuel
  2069.       if lcGate and irisStatus ~= "Offline" then
  2070.         newIris = gate.getIrisHealth()
  2071.         if newIris ~= irisPercent then update = true end
  2072.         irisPercent = newIris
  2073.       end
  2074.       if gateStatus == "Connected" and callDirection == "Outgoing" then updateTimer = os.startTimer(5) else updateTimer = nil end
  2075.     elseif timer == disconTimer then
  2076.       if gateStatus == "Disconnecting" and gateDiscon then
  2077.         gateStatus = "Idle"
  2078.         dialAddress, callDirection = "none", "none"
  2079.         update, monUpdate, gateDiscon, dialFromDHD, disconTimer = true, true, false, false, nil
  2080.         if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  2081.       elseif gateStatus == "Disconnecting" and not gateDiscon then
  2082.         gateDiscon = true
  2083.       end
  2084.       if dialAddress:find("?") then disconTimer = os.startTimer(3) end
  2085.     elseif timer == connectionTimer then
  2086.       connectionTime = connectionTime + 1
  2087.       hours = connectionTime > 35999 and math.floor(connectionTime / 36000) or 0
  2088.       minutes = connectionTime > 599 and math.floor((connectionTime - (hours * 36000)) / 600) or 0
  2089.       sMinutes = minutes > 9 and tostring(minutes) or "0" .. tostring(minutes)
  2090.       seconds = connectionTime > 9 and math.floor((connectionTime - ((hours * 36000) + (minutes * 600))) / 10) or 0
  2091.       sSeconds = seconds > 9 and tostring(seconds) or "0" .. tostring(seconds)
  2092.       sTenths = tostring(math.floor(connectionTime - ((hours * 36000) + (minutes * 600) + (seconds * 10))))
  2093.       connectionClock = sMinutes .. ":" .. sSeconds .. ":" .. sTenths
  2094.       displayConnectionTime()
  2095.       if gateStatus == "Connected" then connectionTimer = os.startTimer(0.1) else connectionTimer = nil end
  2096.     end
  2097.     if update then updateClientAndScreens(monUpdate) end
  2098.   end
  2099. end
  2100.  
  2101. --# AUTOMATIC/STATIC CONFIGURATION (Part 2)
  2102. local function waitingAnimation() --# intentionally not using drawELement - more efficient
  2103.   while true do
  2104.     for i = 5, 21 do
  2105.       term.setCursorPos(i, 11)
  2106.       term.setBackgroundColor(cyan)
  2107.       term.write(" ")
  2108.       term.setBackgroundColor(black)
  2109.       term.setCursorPos(i - 1, 11)
  2110.       term.write(" ")
  2111.       sleep(0.04)
  2112.     end
  2113.     for i = 21, 5, -1 do
  2114.       term.setCursorPos(i, 11)
  2115.       term.setBackgroundColor(cyan)
  2116.       term.write(" ")
  2117.       term.setBackgroundColor(black)
  2118.       term.setCursorPos(i + 1, 11)
  2119.       term.write(" ")
  2120.       sleep(0.04)
  2121.     end
  2122.   end
  2123. end
  2124.  
  2125. local function waitingNet()
  2126.   local id, message, encKey, encodedMessage, encryptedMessage, decryptedMessage, success
  2127.   while true do
  2128.     id, encodedMessage = rednet.receive("ccDHDSetup")
  2129.     if type(encodedMessage) == "string" then
  2130.       success, encryptedMessage = pcall(decode, encodedMessage)
  2131.       if success then
  2132.         encKey = thisCC .. "ccDHD!General_Comms*Key" .. tostring(id)
  2133.         success, decryptedMessage = pcall(decrypt, encKey, encryptedMessage)
  2134.         if success then
  2135.           success, message = pcall(textutils.unserialize, decryptedMessage)
  2136.           if success and type(message) == "table" and message.program == "ccDHD" and message.command == "1stRun" then
  2137.             gateSettings.DHD = id
  2138.             netSend("ccDHDSetup")
  2139.             rednet.unhost("ccDHDSetup", thisGate)
  2140.             return
  2141.           end
  2142.         end
  2143.       end
  2144.     end
  2145.   end
  2146. end
  2147.  
  2148. local function initError(device)
  2149.   term.setBackgroundColor(black)
  2150.   term.clear()
  2151.   drawElement(2, 2, 1, 1, red, black, device == "ONLINE" and "Stargate is OFFLINE!" or "No " .. device .. " detected!")
  2152.   drawElement(2, 4, 1, 1, white, nil, "gateLiaison is OFFLINE")
  2153.   term.setCursorPos(1, 7)
  2154. end
  2155.  
  2156. local function addMonitor(attachName)
  2157.   peripheral.call(attachName, "setTextScale", 1)
  2158.   local tmX, tmY = peripheral.call(attachName, "getSize")
  2159.   if tmX == 29 and tmY == 5 then      --# 3x1 monitor array (marquee)
  2160.     hardware.marquee = hardware.marquee + 1
  2161.     marquee[hardware.marquee] = peripheral.wrap(attachName)
  2162.     if not marquee[hardware.marquee].isColor() then bAndW = true end
  2163.   elseif tmX == 29 and tmY == 19 then --# 3x3 monitor array (gateMon)
  2164.     hardware.gateMon = hardware.gateMon + 1
  2165.     gateMon[hardware.gateMon] = peripheral.wrap(attachName)
  2166.     if not gateMon[hardware.gateMon].isColor() then bAndW = true end
  2167.   elseif tmX == 18 and tmY == 5 then  --# 2x1 monitor array (timerMon)
  2168.     hardware.timerMon = hardware.timerMon + 1
  2169.     timerMon[hardware.timerMon] = peripheral.wrap(attachName)
  2170.     if not timerMon[hardware.timerMon].isColor() then bAndW = true end
  2171.     timerMon[hardware.timerMon].setTextScale(2.5)
  2172.   end
  2173. end
  2174.  
  2175. if pocket then error("Computer or turtle REQUIRED.\n", 0) end
  2176. print("Init Gate...")
  2177. gate = peripheral.find("stargate")
  2178. if not gate then
  2179.   lcGate = true
  2180.   gate = peripheral.find("StargateBase")
  2181.   if gate and not gate.isValid() then gate = nil end
  2182. end
  2183. if not gate then return initError("STARGATE") end
  2184. if tArgs[1] then
  2185.   local validSides = { left = true, right = true, top = true, bottom = true, front = true, back = true }
  2186.   for i = 1, #tArgs do
  2187.     if tArgs[i] == "nomon" then
  2188.       noMon = true
  2189.     elseif tArgs[i] == "outgoing" then
  2190.       outgoingAlarm = true
  2191.     elseif validSides[tArgs[i]] then
  2192.       rsSide = tArgs[i]
  2193.     end
  2194.   end
  2195. end
  2196. if lcGate then
  2197.   local irisStates = {
  2198.     OPEN = "Open";
  2199.     CLOSED = "Closed";
  2200.     OPENING = "Opening";
  2201.     CLOSING = "Closing";
  2202.     NONE = "Offline";
  2203.   }
  2204.   thisGate = gate.getStargateAddressString()
  2205.   --# DETERMINE GATE STATUS (INCOMING, CONNECTED, etc.)
  2206.   --gateStatus = gate.isConnected() and "Connected" or "Idle"
  2207.   --gateStatus = gate.isDialing() and "Dialing" or gateStatus
  2208.   local glyphs = gate.getActivatedGlyphs()  --# returns a string
  2209.   local chevs = gate.getActivatedChevrons() --# returns a number
  2210.   if glyphs and chevs > 0 then              --# this can be confused - if the gate is hanging up, there will be chevrons encoded, but the status is "Disconnecting" not "Dialing"
  2211.     dialAddress = chevs == 9 and glyphs or glyphs .. string.rep("?", 9 - chevs)
  2212.     gateStatus = chevs == 9 and "Connected" or "Dialing"
  2213.     chevronNumber = chevs
  2214.     callDirection = "Unknown"
  2215.     if rsSide ~= "none" and outgoingAlarm then rs.setOutput(rsSide, gateStatus == "Dialing") end
  2216.   else
  2217.     if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  2218.     gateStatus = "Idle"
  2219.     callDirection = "none"
  2220.   end
  2221.   irisStatus = irisStates[gate.getIrisState()]
  2222.   irisPercent = irisStatus ~= "Offline" and gate.getIrisHealth() or 0
  2223. else
  2224.   thisGate = gate.localAddress()
  2225.   local gateState
  2226.   gateState, chevronNumber, callDirection = gate.stargateState()
  2227.   gateStatus = sgStates[gateState] or "Unknown"
  2228.   if gateStatus == "Offline" then return initError("ONLINE") end
  2229.   if gateStatus == "Dialing" or gateStatus == "Connected" then
  2230.     if callDirection == "Incoming" then
  2231.       incomingAddress = gate.remoteAddress()
  2232.       if rsSide ~= "none" then rs.setOutput(rsSide, gateStatus == "Dialing") end
  2233.     elseif callDirection == "Outgoing" then
  2234.       dialAddress = gate.remoteAddress()
  2235.       if rsSide ~= "none" and outgoingAlarm then rs.setOutput(rsSide, gateStatus == "Dialing") end
  2236.     else
  2237.       callDirection = "Unknown"
  2238.       dialAddress = gate.remoteAddress()
  2239.     end
  2240.   elseif gateStatus == "Idle" or gateStatus == "Disconnecting" then
  2241.     if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  2242.     callDirection = "none"
  2243.   end
  2244.   irisStatus = gate.irisState()
  2245. end
  2246. irisState = irisStatus == "Closed"
  2247. waitingForIris = (irisStatus == "Opening" or irisStatus == "Closing")
  2248. print("Init Modem...")
  2249. for _, side in pairs(rs.getSides()) do --# Look for modems before monitors and wireless before wired - stop at the first modem found
  2250.   if peripheral.isPresent(side) and peripheral.getType(side) == "modem" and peripheral.call(side, "isWireless") then
  2251.     wifiComms = true
  2252.     modemSide = side
  2253.     break
  2254.   end
  2255. end
  2256. if modemSide == "none" then
  2257.   for _, side in pairs(rs.getSides()) do
  2258.     if peripheral.isPresent(side) and peripheral.getType(side) == "modem" then
  2259.       for _, name in pairs(peripheral.call(side, "getNamesRemote")) do
  2260.         if peripheral.getType(name) == "computer" and peripheral.call(side, "isPresentRemote", name) and peripheral.call(side, "callRemote", name, "getID") ~= tonumber(thisCC) then
  2261.           modemSide = side
  2262.           break
  2263.         end
  2264.       end
  2265.     end
  2266.     if modemSide ~= "none" then break end
  2267.   end
  2268. end
  2269. if modemSide == "none" then return initError("MODEM") end
  2270. rednet.open(modemSide)
  2271. if noMon then --# turtles can use the full-block modem from cc:Tweaked
  2272.   print("Monitor support disabled...")
  2273. else
  2274.   print("Init Monitors...")
  2275.   for _, side in pairs(rs.getSides()) do --# look for monitors
  2276.     if peripheral.isPresent(side) and peripheral.getType(side) == "modem" and not peripheral.call(side, "isWireless") then
  2277.       for _, name in pairs(peripheral.call(side, "getNamesRemote")) do
  2278.         if peripheral.getType(name) == "monitor" then
  2279.           addMonitor(name)
  2280.         end
  2281.       end
  2282.     elseif peripheral.isPresent(side) and peripheral.getType(side) == "monitor" then
  2283.       addMonitor(side)
  2284.     end
  2285.   end
  2286. end
  2287. if bAndW then --# if a black and white monitor was found, reset monitor colors to black and white
  2288.   msilver = grayScale and colors.lightGray or colors.white
  2289.   mgray = grayScale and colors.gray or colors.black
  2290.   --mbrown = colors.white
  2291.   myellow = colors.white
  2292.   morange = colors.white
  2293.   mred = colors.white
  2294.   --mmagenta = colors.white
  2295.   --mpurple = colors.white
  2296.   mblue = colors.black
  2297.   msky = colors.white
  2298.   mcyan = colors.white
  2299.   --mlime = colors.white
  2300.   mgreen = colors.white
  2301. end
  2302. if marquee[1] then
  2303.   for i = 1, hardware.marquee do
  2304.     marquee[i].setBackgroundColor(mwhite)
  2305.     marquee[i].setTextColor(mblack)
  2306.     marquee[i].clear()
  2307.     marquee[i].setTextScale(2)
  2308.     marquee[i].setCursorPos(2, 1)
  2309.     marquee[i].write("gateLiaison")
  2310.     marquee[i].setCursorPos(2, 2)
  2311.     marquee[i].write("Initializing")
  2312.   end
  2313. end
  2314. if gateMon[1] then
  2315.   for i = 1, hardware.gateMon do
  2316.     gateMon[i].setBackgroundColor(mwhite)
  2317.     gateMon[i].setTextColor(mblack)
  2318.     gateMon[i].clear()
  2319.     gateMon[i].setTextScale(2)
  2320.     gateMon[i].setCursorPos(2, 1)
  2321.     gateMon[i].write("gateLiaison")
  2322.     gateMon[i].setCursorPos(2, 2)
  2323.     gateMon[i].write("Initializing")
  2324.   end
  2325. end
  2326. if timerMon[1] then
  2327.   for i = 1, hardware.timerMon do
  2328.     timerMon[i].setBackgroundColor(mwhite)
  2329.     timerMon[i].setTextColor(mblack)
  2330.     timerMon[i].clear()
  2331.     timerMon[i].setCursorPos(2, 1)
  2332.     timerMon[i].write("Gate")
  2333.     timerMon[i].setCursorPos(1, 2)
  2334.     timerMon[i].write("Init...")
  2335.   end
  2336. end
  2337. print("Inspect Environment...")
  2338. fuelPercent = getFuelLevel()
  2339. if not fs.exists("/data/gateConfig") then --# Check for first time install
  2340.   print("First Run...")
  2341.   if not fs.exists("/data") then fs.makeDir("/data") end
  2342.   if not os.getComputerLabel() then os.setComputerLabel("gateLiaison.cc#" .. thisCC) end
  2343.   term.setBackgroundColor(black)
  2344.   term.clear()
  2345.   drawElement(2, 2, 1, 1, nil, nil, "Please start ccDHD to complete setup.")
  2346.   drawElement(2, 4, 1, 1, nil, nil, "gateLiaison will automatically start")
  2347.   drawElement(2, 5, 1, 1, nil, nil, "after ccDHD is initialized.")
  2348.   drawElement(2, 7, 1, 1, nil, nil, "This gate: " .. thisGate)
  2349.   rednet.host("ccDHDSetup", thisGate)
  2350.   drawElement(2, 9, 1, 1, silver, nil, "...waiting for ccDHD...")
  2351.   parallel.waitForAny(waitingNet, waitingAnimation)
  2352.   term.setBackgroundColor(black)
  2353.   term.clear()
  2354.   local gateConfig = fs.open("/data/gateConfig", "w")
  2355.   gateConfig.write(textutils.serialize(gateSettings))
  2356.   gateConfig.close()
  2357. else
  2358.   print("Ingest Data...")
  2359.   local gateConfig = fs.open("/data/gateConfig", "r")
  2360.   gateSettings = textutils.unserialize(gateConfig.readAll())
  2361.   gateConfig.close()
  2362. end
  2363. ccLabel = os.getComputerLabel():sub(1, 25)
  2364. if marquee[1] or gateMon[1] or timerMon[1] then print("Prepare Monitors...") end
  2365. if marquee[1] then
  2366.   for i = 1, hardware.marquee do
  2367.     marquee[i].setBackgroundColor(mblack)
  2368.     marquee[i].clear()
  2369.   end
  2370. end
  2371. if timerMon[1] then
  2372.   for i = 1, hardware.timerMon do
  2373.     timerMon[i].setBackgroundColor(mblack)
  2374.     timerMon[i].clear()
  2375.   end
  2376. end
  2377. if gateMon[1] and (not bAndW or (bAndW and grayScale)) then
  2378.   local gateImage = {
  2379.     {},
  2380.     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, },
  2381.     { 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, 128, 128, 256, 128, 128, 128, 128, 128, 128, 128, },
  2382.     { 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, },
  2383.     { 0, 0, 0, 0, 0, 128, 128, 256, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 256, 128, 128, },
  2384.     { 0, 0, 0, 0, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, },
  2385.     { 0, 0, 0, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, },
  2386.     { 0, 0, 128, 128, 256, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 256, 128, 128, },
  2387.     { 0, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, },
  2388.     { 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, },
  2389.     { 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, },
  2390.     { 0, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, },
  2391.     { 0, 0, 128, 128, 256, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 256, 128, 128, },
  2392.     { 0, 0, 0, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, },
  2393.     { 0, 0, 0, 0, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, },
  2394.     { 0, 0, 0, 0, 0, 128, 128, 256, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 256, 128, 128, },
  2395.     { 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, },
  2396.     { 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, },
  2397.   }
  2398.   gateMon[1].setTextScale(1)
  2399.   gateMon[1].setBackgroundColor(mblack)
  2400.   gateMon[1].clear()
  2401.   local termPrime = term.redirect(gateMon[1])
  2402.   paintutils.drawImage(gateImage, 1, 1)
  2403.   if gateMon[2] then
  2404.     for i = 2, hardware.gateMon do
  2405.       gateMon[i].setTextScale(1)
  2406.       gateMon[i].setBackgroundColor(mblack)
  2407.       gateMon[i].clear()
  2408.       term.redirect(gateMon[i])
  2409.       paintutils.drawImage(gateImage, 1, 1)
  2410.     end
  2411.   end
  2412.   term.redirect(termPrime)
  2413. end
  2414. displayConnectionTime()
  2415. term.setBackgroundColor(black)
  2416. term.clear()
  2417. updatePercentages(true, true)
  2418. if gateStatus == "Connected" then
  2419.   local dataPack = textutils.serialize({ irisState = irisState and "closed" or "open" })
  2420.   pcall(gate.sendMessage, dataPack)
  2421.   if callDirection == "Outgoing" then updateTimer = os.startTimer(5) end
  2422.   connectionTimer = os.startTimer(0.1)
  2423. end
  2424. --# END AUTOMATIC/STATIC CONFIGURATION (Part 2)
  2425. if lcGate then
  2426.   parallel.waitForAny(gateReceive, netReceive, lcChevronEncoder, lcGateMonitor, lcIrisMonitor, dataPoller, charInput)
  2427. else
  2428.   parallel.waitForAny(gateReceive, netReceive, incomingCall, outgoingCall, sgChevronEncoder, sgChevronUnencoded, sgGateMonitor, sgIrisMonitor, dataPoller, charInput)
  2429. end
Add Comment
Please, Sign In to add comment