Advertisement
HydrantHunter

ccDHD 1.5

Jun 4th, 2014
4,057
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 114.89 KB | None | 0 0
  1. --[[  LanteaCraft      ]]--
  2. --[[      and SGCraft  ]]--
  3. --[[     c c D H D     ]]--
  4. --[[      by Dog       ]]--
  5. --[[ aka HydrantHunter ]]--
  6. --[[  with help from   ]]--
  7. --[[    Bomb Bloke     ]]--
  8. --[[ pastebin jriyySTL ]]--
  9. local ccDHDVer = "1.5.02"
  10. --[[
  11. Tested with/requires:
  12.   - Minecraft 1.7.10
  13.   - LanteaCraft-1.7.10-70 || SGCraft1.95-mc1.7.10
  14.   - ComputerCraft 1.73, 1.74, 1.75
  15.     - An advanced computer with a wireless modem
  16.       - 1 or more 3x and/or 1x advanced monitor arrays [optional]
  17.     - gateLiaison running on a computer (standard or advanced, with a wireless modem) or a wireless turtle (standard or advanced)
  18.   - 1 or more Biometric Locks (Gopher's Peripherals 2.2) [optional]
  19. ]]--
  20. --# Default Settings
  21. local settingsData = "/data/DHDconfig"
  22. local gateData = "/data/DHDgates"
  23. local gateHistory = "/data/DHDhistory"
  24. local lastGate = "/data/DHDlastCall"
  25. local dhdSettings = {
  26.   highlight = true,
  27.   password = "password",
  28.   bio = { lock = false, func = "none", auth = 4, fAuth = 2, },
  29.   gate = 65536,
  30.   pSync = false,
  31.   ecIris = false,    --# open iris upon disconnect
  32.   startIris = false, --# now used for incoming call auto-iris-close
  33.   logs = true,
  34. }
  35. local updateStatus, secKernel, drawCLI, drawHeader, drawSettingsUI, drawWaitingIrisState, drawRatingList, assignRating, assignColor, pSyncClient, selectedGate, bCurX, bCurY, curColor, bColor, word, secWord
  36. local oldOsPullEvent --# reserved for bioLogin and lockdown mode, to temporarily suspend CTRL-T/terminate
  37. --# Peripherals
  38. local termX, termY = term.getSize()    --# standard 51x19 / tab = 51x18
  39. local hardware = { modemSide = "none", bio = 0, listMon = 0, marquee = 0 } --# Modem, Biolock(s), and Monitor(s)
  40. local listMon, listMonSides = { }, { } --# List monitor
  41. local marquee, marqueeSides = { }, { } --# Marquee Monitor
  42. local thisGate                         --# this gate's address
  43. --# Status Info
  44. local gateStatus, secureStatus, displayState, runState, currentState, dialAddress = "QRY", "QRY", "list", "init", "init", "none"
  45. local menuState, kernelState, irisState, lcGate, fistAuth = false, false, false, false, false
  46. local configChange, gateChange, waitingForIris, gettingInput, redraw = false, false, false, false, false
  47. local currentEdit, incomingAddress, fuelGauge
  48. local chevronNumber, SG_MAXFUEL = 0, 203842
  49. --# Address Book
  50. local addressBook = { { name = "NEW GATE", addr = "ADDRESS", rating = "U", iris = "none", note = "short note", loc = { x = 99999, y = 99999, z = 99999, dim = "Overworld", }, }, }
  51. local gatePages, gatePage, listPages, listPage = 1, 1, 1, 1
  52. local pNum = tostring(gatePage) .. " of " .. tostring(gatePages)
  53.  --# Call History
  54. local logPages, logPage = 1, 1
  55. local callHistory = { }
  56. local lastCall
  57. --# Color Definitions
  58. local white = colors.white
  59. local silver = colors.lightGray
  60. local gray = colors.gray
  61. local black = colors.black
  62. local brown = colors.brown
  63. local yellow = colors.yellow
  64. local orange = colors.orange
  65. local red = colors.red
  66. local magenta = colors.magenta
  67. local purple = colors.purple
  68. local blue = colors.blue
  69. local sky = colors.lightBlue
  70. local cyan = colors.cyan
  71. local lime = colors.lime
  72. local green = colors.green
  73.  
  74. local function clearMonitors()
  75.   if hardware.listMon > 0 then
  76.     for i = 1, hardware.listMon do
  77.       listMon[i].setBackgroundColor(black)
  78.       listMon[i].clear()
  79.     end
  80.   end
  81.   if hardware.marquee > 0 then
  82.     for i = 1, hardware.marquee do
  83.       marquee[i].setBackgroundColor(black)
  84.       marquee[i].clear()
  85.     end
  86.   end
  87. end
  88.  
  89. local function clearScreen(bgColor)
  90.   term.setBackgroundColor(bgColor or black)
  91.   term.clear()
  92. end
  93.  
  94. local function clearLowerScreen(bgColor)
  95.   term.setBackgroundColor(bgColor or black)
  96.   local line = string.rep(" ", termX)
  97.   for i = 5, termY do
  98.     term.setCursorPos(1, i)
  99.     term.write(line)
  100.   end
  101. end
  102.  
  103. local function shutDown()
  104.   clearMonitors()
  105.   if hardware.modemSide ~= "none" then
  106.     if dhdSettings.pSync then
  107.       rednet.unhost("pSync")
  108.     end
  109.     rednet.close(hardware.modemSide)
  110.   end
  111. end
  112.  
  113. local function recordSessionData()                        --# Human readable log files (last gate & history)
  114.   local logAddress = incomingAddress or dialAddress       --# set logAddress
  115.   logAddress = #logAddress == 7 and logAddress .. thisGate:sub(8, 9) or logAddress --# if logAddress is 7 characters (it is in the same dimension) add thisGate's dimension to the end for logging
  116.   local callDirection = incomingAddress and "Inbound" or "Outbound" --# set callDirection
  117.   local callTime = textutils.formatTime(os.time(), false) --# format the time
  118.   if #callTime == 7 then callTime = " " .. callTime end   --# move single digit hour times to the right one space
  119.   local dataLine = tostring(os.day()) .. " @ " .. callTime .. " <" .. callDirection .. "> " .. logAddress --# build the data line to be recorded
  120.   local previousCall = fs.open(lastGate, "w")             --# record last call
  121.   previousCall.writeLine(dataLine)
  122.   previousCall.close()
  123.   if dhdSettings.logs then                                --# record call in main log file if logs is turned on
  124.     local callArchive = fs.open(gateHistory, fs.exists(gateHistory) and "a" or "w")
  125.     callArchive.writeLine(dataLine)
  126.     callArchive.close()
  127.   end
  128. end
  129.  
  130. local function paginateGates()
  131.   gatePages = math.ceil(#addressBook / 24)
  132.   listPages = math.ceil(#addressBook / 8)
  133. end
  134.  
  135. local function mergeAddressBooks(newGates)
  136.   local matchFound = false                            --# use matchFound to indicate if a matching gate was found or not
  137.   for i = 1, #newGates do                             --# start cycling through the 'new' list of gates
  138.     for j = 1, #addressBook do                        --# search the address book for a matching address
  139.       if newGates[i].addr == addressBook[j].addr then --# if the gate is already in the address book...
  140.         matchFound = true                             --# ...set matchFound to true and...
  141.         break                                         --# stop the address book search loop and move on to the next gate in the 'new' list
  142.       end
  143.     end
  144.     if not matchFound then                            --# if a match wasn't found...
  145.       addressBook[#addressBook + 1] = { }             --# initialize a new address book entry
  146.       for k, v in pairs(newGates[i]) do               --# loop through the gate entries...
  147.         addressBook[#addressBook][k] = v              --# ...and add them to the new address book entry
  148.       end
  149.       if not addressBook[#addressBook].iris then
  150.         addressBook[#addressBook].iris = "none"
  151.       end
  152.       gateChange = true                               --# indicate that address book data has changed
  153.     else
  154.       matchFound = false                              --# if a match was found, reset the variable for the next iteration of the loop
  155.     end
  156.   end
  157.   paginateGates()                                     --# repaginate gates
  158. end
  159.  
  160. local function pSync(pushpull) --# Sync addressBook with an advanced pocket computer running ccDialer
  161.   if pushpull == "pPull" and secureStatus == "allclear" then
  162.     sleep(0.1)
  163.     rednet.send(pSyncClient, addressBook)
  164.   elseif pushpull == "pPush" and secureStatus == "allclear" then
  165.     local id, message = rednet.receive()
  166.     if type(message) == "table" and message.program == "pSync" and id == pSyncClient then
  167.       mergeAddressBooks(message.command)
  168.       redraw = true
  169.       drawCLI(true)
  170.     end
  171.   elseif pushpull == "QRY" then
  172.     sleep(0.1)
  173.     if secureStatus == "allclear" then
  174.       rednet.send(pSyncClient, thisGate)
  175.     else
  176.       rednet.send(pSyncClient, "lockdown")
  177.     end
  178.   end
  179. end
  180.  
  181. local function redrawInput()      --# if we're getting input from the user, account for CLI redraws repositioning the cursor
  182.   term.setTextColor(curColor)     --# set the proper text color
  183.   term.setBackgroundColor(bColor) --# set the proper background color
  184.   term.setCursorPos(bCurX, bCurY) --# reposition the cursor
  185.   term.write(secureStatus == "allclear" and word or secWord) --# re-write input to screen
  186. end
  187.  
  188. local function netSend(thisCommand)
  189.   if not rednet.isOpen(hardware.modemSide) then
  190.     rednet.open(hardware.modemSide)
  191.   end
  192.   local dataPack = { program = "ccDHD", thisGate = thisGate, command = thisCommand }
  193.   rednet.send(dhdSettings.gate, dataPack)
  194. end
  195.  
  196. local function netReceive()
  197.   while true do
  198.     local id, message = rednet.receive()
  199.     if type(message) == "table" then
  200.       if message.program == "ccDHD" and id == dhdSettings.gate then
  201.         if menuState then redraw = true end --# this is just to trigger the propper redraw later
  202.         updateStatus(message)
  203.         if message.gateStatus == "Disconnecting" and dhdSettings.ecIris and irisState and secureStatus == "allclear" then
  204.           if runState == "Dial" or runState == "goPage" or runState == "importExport" or runState == "exodus" then
  205.             drawWaitingIrisState(true)
  206.             if gettingInput then redrawInput() end
  207.           end
  208.           netSend("iOPEN")
  209.           waitingForIris = true
  210.         end
  211.       elseif message.program == "pSync" and dhdSettings.pSync then
  212.         if message.command then
  213.           pSyncClient = id
  214.           if message.command == "pPull" or message.command == "pPush" then
  215.             if thisGate == message.gate then
  216.               pSync(message.command)
  217.               if runState == "Dial" then return end --# cycle dhdKernel so we have the correct input routine (when forced out of a GateEdit during a pPush)
  218.             end
  219.           elseif message.command == "QRY" then
  220.             pSync(message.command)
  221.           else
  222.             if #tostring(message.command) == 7 or #tostring(message.command) == 9 then
  223.               if message.command ~= thisGate then
  224.                 if message.command ~= "endCall" and gateStatus == "Disconnected" then
  225.                   dialAddress = message.command
  226.                   netSend(message.command)
  227.                 elseif message.command == "endCall" then
  228.                   netSend(message.command)
  229.                 end
  230.               end
  231.             end
  232.           end
  233.         elseif message.password then
  234.           local dataPack = { program = "pSync", password = message.password }
  235.           rednet.send(dhdSettings.gate, dataPack)
  236.         end
  237.       end
  238.     else
  239.       if id == dhdSettings.gate and message == dhdSettings.password then
  240.         if runState == "Dial" or runState == "goPage" or runState == "importExport" or runState == "exodus" then
  241.           drawWaitingIrisState(true)
  242.           if gettingInput then redrawInput() end
  243.         end
  244.         netSend("iOPEN")
  245.         waitingForIris = true
  246.       end
  247.     end
  248.   end
  249. end
  250.  
  251. do
  252.   local validStates = {
  253.     Dial = true,
  254.     Trim = true,
  255.     DHDsettings = true,
  256.     newPassword = true,
  257.     GateEdit = true,
  258.     goPage = true,
  259.     goTrimPage = true,
  260.     importExport = true,
  261.     logs = true,
  262.     clearLogs = true,
  263.     goLogPage = true,
  264.     exodus = true,
  265.   }
  266.  
  267.   updateStatus = function(newInfo)
  268.     local monUpdate = false
  269.     if waitingForIris and irisState ~= newInfo.irisState then
  270.       waitingForIris = false
  271.     end
  272.     irisState = newInfo.irisState
  273.     local recordInfo = false
  274.     if currentState == "DHD" then
  275.       local iLength = #tostring(incomingAddress)
  276.       if newInfo.gateStatus == "Connected" and gateStatus ~= newInfo.gateStatus and (dialAddress ~= "none" or iLength == 7 or iLength == 9) then
  277.         recordInfo = true
  278.       end
  279.     else
  280.       lcGate = newInfo.lcGate
  281.       thisGate = newInfo.thisGate
  282.       currentState = "DHD" --# one of the last steps of initialization - we don't record connections that are already established upon startup
  283.     end
  284.     secureStatus = newInfo.secureStatus
  285.     fuelGauge = newInfo.fuelGauge
  286.     if gateStatus ~= newInfo.gateStatus then monUpdate = true end
  287.     if dialAddress ~= newInfo.dialAddress then monUpdate = true end
  288.     if incomingAddress ~= newInfo.incomingAddress then monUpdate = true end
  289.     if chevronNumber ~= newInfo.chevronNumber then monUpdate = true end
  290.     gateStatus = newInfo.gateStatus
  291.     dialAddress = newInfo.dialAddress
  292.     incomingAddress = newInfo.incomingAddress
  293.     chevronNumber = newInfo.chevronNumber
  294.     --# Added for Yanniclord
  295.     local rsSide = "none"
  296.     if incomingAddress and gateStatus == "Dialing" and rsSide ~= "none" then
  297.       rs.setOutput(rsSide, true)
  298.     elseif incomingAddress and gateStatus == "Connected" and rsSide ~= "none" then
  299.       rs.setOutput(rsSide, false)
  300.     end
  301.     if gateStatus == "Disconnected" or gateStatus == "Disconnecting" then
  302.       if rsSide ~= "none" then rs.setOutput(rsSide, false) end
  303.     end
  304.     --# END
  305.     local irisClose = false
  306.     local irisManage = false
  307.     --if incomingAddress and dhdSettings.startIris then irisClose = true end
  308.     if lcGate then
  309.       if incomingAddress and recordInfo and secureStatus == "allclear" then
  310.         if dhdSettings.startIris then irisClose = true end
  311.         irisManage = true
  312.       end
  313.     else
  314.       if incomingAddress and (#incomingAddress == 7 or #incomingAddress == 9) and secureStatus == "allclear" and gateStatus == "Dialing" then
  315.         if dhdSettings.startIris then irisClose = true end
  316.         irisManage = true
  317.       end
  318.     end
  319.     if irisManage then
  320.       for i = 1, #addressBook do
  321.         if addressBook[i].addr == incomingAddress or (addressBook[i].addr:sub(1, 7) == incomingAddress and addressBook[i].addr:sub(8, 9) == thisGate:sub(8, 9)) then
  322.           if addressBook[i].iris == "open" then
  323.             if irisState then
  324.               if runState == "Dial" or runState == "goPage" or runState == "importExport" or runState == "exodus" then
  325.                 drawWaitingIrisState(true)
  326.                 if gettingInput then redrawInput() end
  327.               end
  328.               waitingForIris = true
  329.               netSend("iOPEN")
  330.             end
  331.             irisClose = false
  332.           elseif addressBook[i].iris == "close" then
  333.             if not irisState then
  334.               if runState == "Dial" or runState == "goPage" or runState == "importExport" or runState == "exodus" then
  335.                 drawWaitingIrisState()
  336.                 if gettingInput then redrawInput() end
  337.               end
  338.               waitingForIris = true
  339.               netSend("iCLOSE")
  340.             end
  341.             irisClose = false
  342.           end
  343.           break
  344.         end
  345.       end
  346.     end
  347.     if incomingAddress and dhdSettings.startIris and irisClose and gateStatus == "Dialing" and not irisState then
  348.       if runState == "Dial" or runState == "goPage" or runState == "importExport" or runState == "exodus" then
  349.         drawWaitingIrisState()
  350.         if gettingInput then redrawInput() end
  351.       end
  352.       waitingForIris = true
  353.       netSend("iCLOSE")
  354.     end
  355.     if recordInfo then recordSessionData() end
  356.     if validStates[runState] then
  357.       drawCLI(monUpdate)
  358.     end
  359.   end
  360. end
  361.  
  362. local function saveData(fileName, fileType)
  363.   menuState = false
  364.   if fs.exists("/disk") and fileName == ("/disk/data/DHDgates") then
  365.     if not fs.exists("/disk/data") then fs.makeDir("/disk/data") end
  366.   end
  367.   local dhdData = fs.open(tostring(fileName), "w")
  368.   if fileType == "cfg" then
  369.     dhdData.write(textutils.serialize(dhdSettings))
  370.     configChange = false
  371.   elseif fileType == "gate" then
  372.     dhdData.write(textutils.serialize(addressBook))
  373.     if fileName == gateData then gateChange = false end
  374.   end
  375.   dhdData.close()
  376. end
  377.  
  378. local function ingestData(fileName, fileType)
  379.   if fileName == "logs" then                        --# Logs
  380.     if fs.exists(gateHistory) then
  381.       for log_entry in io.lines(gateHistory) do
  382.         table.insert(callHistory, 1, log_entry)
  383.       end
  384.       logPage, logPages = 1, math.max(1, math.ceil(#callHistory / 11)) --# paginate call logs
  385.     end
  386.     if fs.exists(lastGate) then
  387.       local dhdLast = fs.open(lastGate, "r")
  388.       lastCall = dhdLast.readLine()
  389.       dhdLast.close()
  390.     end
  391.   else
  392.     if not fs.exists(fileName) then return end
  393.     local dhdData = fs.open(fileName, "r")
  394.     local dhdInfo = dhdData.readAll()
  395.     dhdData.close()
  396.     if fileType == "cfg" then                       --# Config
  397.       dhdSettings = textutils.unserialize(dhdInfo)
  398.     elseif fileType == "gate" then                  --# Address book (main or backup)
  399.       for i = #addressBook, 1, -1 do
  400.         addressBook[i] = nil
  401.       end
  402.       addressBook = textutils.unserialize(dhdInfo)
  403.       gateChange = true                             --# a change has occured, even though gateChange *might* already be TRUE...
  404.       local fixNames = false                        --# ...so we use fixNames to track if any *new* changes have occured
  405.       for i = 1, #addressBook do
  406.         if not addressBook[i].name then             --# we've found an 'old' address book (before names were added)
  407.           addressBook[i].name = addressBook[i].addr --# add name to entry
  408.           fixNames = true                           --# note that changes have occured
  409.         end
  410.         if not addressBook[i].iris then             --# we've found an 'old' address book (before iris control was added)
  411.           addressBook[i].iris = "none"              --# add default iris control setting to entry
  412.           fixNames = true                           --# note that changes have occured
  413.         end
  414.       end
  415.       paginateGates()
  416.       gatePage, listPage = 1, 1
  417.       if fixNames and fileName == gateData then saveData(gateData, "gate") end --# if we're working with the main address book and there are new changes, then save the updated address book
  418.       if fileName == gateData then gateChange = false end --# if we're working with the main address book, not that there are no changes now (memory and disk match each other)
  419.     end
  420.   end
  421. end
  422.  
  423. local function mergeData()                            --# merge two address books into one
  424.   if not fs.exists("/disk/data/DHDgates") then return end
  425.   local dhdData = fs.open("/disk/data/DHDgates", "r")
  426.   local newGates = textutils.unserialize(dhdData.readAll())
  427.   dhdData.close()
  428.   mergeAddressBooks(newGates)
  429. end
  430.  
  431. do
  432.   local colorBurst = {
  433.     B = { blue, "Base/Outpost/Hub" },
  434.     H = { sky, "Home/Camp" },
  435.     V = { brown, "Village" },
  436.     M = { purple, "Misc/Special" },
  437.     S = { green, "Safe/Secured" },
  438.     C = { orange, "Caution" },
  439.     D = { red, "Danger" },
  440.     U = { silver, "Unknown/Unclassified" }
  441.   }
  442.  
  443.   assignColor = function(gateNumber)
  444.     return colorBurst[addressBook[gateNumber].rating][1]
  445.   end
  446.  
  447.   assignRating = function(gateNumber)
  448.     return colorBurst[addressBook[gateNumber].rating][2]
  449.   end
  450. end
  451.  
  452. local function makeLongName(name)
  453.   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) })
  454. end
  455.  
  456. local function displayMarquee()
  457.   if hardware.marquee > 0 then
  458.     for i = 1, hardware.marquee do
  459.       marquee[i].setTextScale(2)
  460.       marquee[i].setBackgroundColor(black)
  461.       marquee[i].clear()
  462.       marquee[i].setTextColor(cyan)
  463.       marquee[i].setCursorPos(1, 1)
  464.       marquee[i].write("Stargate")
  465.       marquee[i].setTextColor(yellow)
  466.       marquee[i].setCursorPos(4, 2)
  467.       marquee[i].write(thisGate)
  468.     end
  469.   end
  470. end
  471.  
  472. local function displayAddressBook()
  473.   if hardware.listMon > 0 then
  474.     local firstGate = (listPage - 1) * 8 + 1
  475.     for i = 1, hardware.listMon do
  476.       local tmY = 1
  477.       listMon[i].setTextScale(0.5)
  478.       listMon[i].setBackgroundColor(black)
  479.       listMon[i].clear()
  480.       for j = firstGate, firstGate + 7 do
  481.         if j > #addressBook then break end
  482.         listMon[i].setCursorPos(4, tmY)
  483.         listMon[i].setTextColor(assignColor(j))
  484.         if (thisGate == addressBook[j].addr or addressBook[j].addr == thisGate:sub(1, 7)) and dhdSettings.highlight then
  485.           listMon[i].setTextColor(gray)
  486.         end
  487.         listMon[i].write(addressBook[j].name)
  488.         tmY = tmY + 1
  489.         if tmY >= 9 then break end
  490.       end
  491.       listMon[i].setBackgroundColor(cyan)
  492.       listMon[i].setTextColor(white)
  493.       listMon[i].setCursorPos(1, 10)
  494.       listMon[i].write("   /\\     \\/   ")
  495.     end
  496.   end
  497. end
  498.  
  499. local function displayNotes(gateID)
  500.   if hardware.listMon > 0 then
  501.     for i = 1, hardware.listMon do
  502.       listMon[i].setBackgroundColor(black)
  503.       listMon[i].clear()
  504.       if #addressBook[gateID].addr == 9 then
  505.         listMon[i].setTextScale(0.5)
  506.         listMon[i].setCursorPos(4, 5)
  507.       else
  508.         listMon[i].setTextScale(1)
  509.         listMon[i].setCursorPos(1, 2)
  510.       end
  511.       listMon[i].setTextColor(assignColor(gateID))
  512.       listMon[i].write(addressBook[gateID].addr)
  513.       if addressBook[gateID].addr == thisGate or addressBook[gateID].addr == thisGate:sub(1, 7) then
  514.         listMon[i].setTextColor(gray)
  515.         if #addressBook[gateID].addr == 9 then
  516.           listMon[i].setCursorPos(4, 7)
  517.           listMon[i].write("THIS GATE")
  518.         else
  519.           listMon[i].setCursorPos(2, 4)
  520.           listMon[i].write("THIS")
  521.           listMon[i].setCursorPos(3, 5)
  522.           listMon[i].write("GATE")
  523.         end
  524.       end
  525.     end
  526.   end
  527.   if hardware.marquee > 0 then
  528.     for i = 1, hardware.marquee do
  529.       marquee[i].setTextScale(1)
  530.       marquee[i].setBackgroundColor(black)
  531.       marquee[i].clear()
  532.       marquee[i].setCursorPos(1, 2)
  533.       marquee[i].setTextColor(sky)
  534.       marquee[i].write(addressBook[gateID].loc.dim)
  535.       marquee[i].setCursorPos(1, 4)
  536.       marquee[i].setTextColor(white)
  537.       if #addressBook[gateID].note > 29 then
  538.         marquee[i].write(addressBook[gateID].note:sub(1, 29))
  539.         marquee[i].setCursorPos(2, 5)
  540.         marquee[i].write(addressBook[gateID].note:sub(30, 43))
  541.       else
  542.         marquee[i].write(addressBook[gateID].note)
  543.       end
  544.     end
  545.   end
  546. end
  547.  
  548. local function displayStatus()
  549.   if hardware.marquee > 0 then
  550.     for i = 1, hardware.marquee do
  551.       marquee[i].setTextScale(2)
  552.       marquee[i].setBackgroundColor(black)
  553.       marquee[i].setCursorPos(1, 1)
  554.       if gateStatus == "Dialing" then
  555.         if incomingAddress then --# incoming call
  556.           marquee[i].setTextColor(secureStatus == "allclear" and yellow or red)
  557.           marquee[i].write("Incoming from ")
  558.           marquee[i].setTextColor(secureStatus == "allclear" and sky or orange)
  559.           if lcGate then        --# LanteaCraft
  560.             marquee[i].setCursorPos(4, 2)
  561.             marquee[i].write(incomingAddress .. string.rep(" ", 9))
  562.           else                  --# SGCraft
  563.             marquee[i].setCursorPos(#incomingAddress - 5, 2)
  564.             if #incomingAddress == 7 then
  565.               local longAddress = makeLongName(incomingAddress)
  566.               marquee[i].write(longAddress:sub(1, chevronNumber * 2))
  567.               marquee[i].setTextColor(gray)
  568.               marquee[i].write(longAddress:sub((chevronNumber * 2) + 1, #longAddress))
  569.             else
  570.               marquee[i].write(incomingAddress:sub(1, chevronNumber))
  571.               marquee[i].setTextColor(gray)
  572.               marquee[i].write(incomingAddress:sub(chevronNumber + 1, #incomingAddress))
  573.             end
  574.           end
  575.         else                    --# outgoing call
  576.           marquee[i].setTextColor(sky)
  577.           marquee[i].write("Dialing ")
  578.           marquee[i].setCursorPos(#dialAddress - 5, 2)
  579.           marquee[i].setTextColor(yellow)
  580.           if #dialAddress == 7 then
  581.             local longAddress = makeLongName(dialAddress)
  582.             marquee[i].write(longAddress:sub(1, chevronNumber * 2))
  583.             marquee[i].setTextColor(gray)
  584.             marquee[i].write(longAddress:sub((chevronNumber * 2) + 1, #longAddress))
  585.           else
  586.             marquee[i].write(dialAddress:sub(1, chevronNumber))
  587.             marquee[i].setTextColor(gray)
  588.             marquee[i].write(dialAddress:sub(chevronNumber + 1, #dialAddress))
  589.           end
  590.         end
  591.       elseif gateStatus == "Connected" then
  592.         if incomingAddress then --# incoming call
  593.           marquee[i].setTextColor(secureStatus == "allclear" and yellow or red)
  594.           marquee[i].write("Incoming from ")
  595.           marquee[i].setTextColor(secureStatus == "allclear" and sky or orange)
  596.           marquee[i].setCursorPos(#incomingAddress - 5, 2)
  597.           marquee[i].write(makeLongName(incomingAddress) .. string.rep(" ", 9))
  598.         else                    --# outgoing call
  599.           marquee[i].setTextColor(sky)
  600.           if dialAddress ~= "none" and dialAddress ~= "n o n e" and gateStatus ~= "Disconnecting" then
  601.             marquee[i].write("Connected to")
  602.             marquee[i].setTextColor(yellow)
  603.             marquee[i].setCursorPos(#dialAddress - 5, 2)
  604.             marquee[i].write(makeLongName(dialAddress))
  605.           end
  606.         end
  607.       elseif gateStatus == "Disconnecting" then
  608.         marquee[i].clear()
  609.         marquee[i].setTextColor(sky)
  610.         marquee[i].write(gateStatus)
  611.       end
  612.     end
  613.   end
  614.   if gateStatus == "Disconnected" and secureStatus == "allclear" then
  615.     if displayState == "list" then
  616.       displayMarquee()
  617.       displayAddressBook()
  618.     elseif displayState == "info" then
  619.       displayNotes(selectedGate)
  620.     end
  621.   end
  622. end
  623.  
  624. local function drawElement(x, y, w, h, txtColor, bgColor, text)
  625.   if type(w) == "string" then
  626.     term.setCursorPos(x, y)
  627.     term.setBackgroundColor(green)      --# Switch (Neutral)
  628.     term.write(" ")
  629.     term.setBackgroundColor(gray)
  630.     term.write("  ")
  631.     term.setBackgroundColor(red)
  632.     term.write(" ")
  633.   elseif type(w) == "boolean" then
  634.     term.setCursorPos(x, y)
  635.     if w then                           --# Switch (ON)
  636.       term.setBackgroundColor(green)
  637.       term.write("  ")
  638.       term.setBackgroundColor(gray)
  639.       term.write("  ")
  640.     else                                --# Switch (OFF)
  641.       term.setBackgroundColor(gray)
  642.       term.write("  ")
  643.       term.setBackgroundColor(red)
  644.       term.write("  ")
  645.     end
  646.   else
  647.     if bgColor then term.setBackgroundColor(bgColor) end
  648.     local deText = text and tostring(text) or ""
  649.     if w > #deText or h > 1 then       --# We're drawing something more than text
  650.       local line = string.rep(" ", w)  --# Define one line of the 'element' (box/rectangle/line-seg)
  651.       for i = y, y + h - 1 do
  652.         term.setCursorPos(x, i)        --# Position cursor
  653.         term.write(line)               --# Draw 1 of h lines of the 'element' (box/rectangle/line-seg)
  654.       end
  655.     end
  656.     if deText ~= "" then
  657.       if txtColor then term.setTextColor(txtColor) end
  658.       w = math.max(w, #deText)         --# Ensure minimum length
  659.       local xW = (x + math.floor(w / 2)) - math.floor(#deText / 2) --# Center the text horizontally
  660.       local yH = y + math.floor(h / 2) --# Center the text vertically
  661.       term.setCursorPos(xW, yH)        --# Set the cursor position
  662.       term.write(deText)               --# Write the text
  663.     end
  664.   end
  665. end
  666.  
  667. drawWaitingIrisState = function(_open)
  668.   drawElement(43, 7, 1, 1, _open and orange or green, sky, "Iris")
  669. end
  670.  
  671. do
  672.   local runStates = {
  673.     Dial = blue,
  674.     goPage = blue,
  675.     importExport = blue,
  676.     exodus = blue,
  677.     Trim = orange,
  678.     goTrimPage = orange,
  679.     GateEdit = brown,
  680.   }
  681.  
  682.   drawHeader = function()
  683.     local bgColor
  684.     term.setBackgroundColor(black)
  685.     term.setTextColor(white)
  686.     if secureStatus == "allclear" then
  687.       for k, v in pairs(runStates) do
  688.         if tostring(k) == runState then
  689.           bgColor = v
  690.         end
  691.       end
  692.     elseif secureStatus == "lockdown" then
  693.       bgColor = red
  694.     end
  695.     if (runState ~= "logs" and runState ~= "clearLogs" and runState ~= "goLogPage") or secureStatus == "lockdown" then --# header for all but logs
  696.       drawElement(1, 1, termX, 1, white, bgColor, thisGate)
  697.     else                        --# If we're viewing the logs, show the last call info instead of connect/iris info
  698.       drawElement(1, 1, termX, 1, nil, yellow, "")                                              --# yellow header                                                      
  699.       drawElement(1, 2, termX, 3, nil, gray, "")                                                --# sub-header body
  700.       drawElement((termX / 2) - 7, 1, 16, 1, yellow, nil, thisGate .. " Logs")      --# header text
  701.       drawElement(2, 2, 1, 1, silver, nil, "Last Call:")
  702.       drawElement(2, 4, 25, 1, nil, nil, "Day    Time       Vector     Address")
  703.       drawElement(termX - 6, 2, 7, 1, red, silver, " Close ")
  704.       drawElement(termX - 6, 4, 7, 1, orange, nil, " Clear ")
  705.       term.setBackgroundColor(gray)
  706.       term.setTextColor(black)
  707.       term.setCursorPos(2, 3)
  708.       if not lastCall then
  709.         term.write("none" .. string.rep(" ", 3) .. "none" .. string.rep(" ", 7) .. "none" .. string.rep(" ", 7) .. "none")
  710.       else
  711.         term.write(lastCall:sub(1, string.find(lastCall, "@") - 2))                             --# Day
  712.         term.setCursorPos(9, 3)
  713.         term.write(lastCall:sub(string.find(lastCall, "@") + 2, string.find(lastCall, "M")))    --# Time
  714.         term.setCursorPos(20, 3)
  715.         local callDir = (lastCall:sub(string.find(lastCall, "<") + 1, string.find(lastCall, ">") - 1))
  716.         term.write(callDir)                                                                     --# Direction
  717.         term.setTextColor(callDir == "Outbound" and sky or cyan)
  718.         term.setCursorPos(31, 3)
  719.         term.write(lastCall:sub(string.find(lastCall, ">") + 2))                                --# Address
  720.       end
  721.       return
  722.     end
  723.     if (runState == "Dial" or runState == "importExport" or runState == "goPage" or runState == "exodus") and secureStatus == "allclear" then  --# Dial mode gets the -DHD- menu
  724.       drawElement(2, 1, 5, 1, white, black, "-DHD-")                                            --# Drop down menu button
  725.     end
  726.     drawElement(1, 2, termX, 3, nil, gray, "")                                                  --# Draw 'status' area
  727.     drawElement(2, 3, 7, 1, silver, nil, "Fuel:")
  728.     drawElement(20, 3, 5, 1, nil, nil, "Gate:")
  729.     --# Fuel & Gate Status
  730.     if lcGate then
  731.       drawElement(9, 3, 1, 1, fuelGauge and lime or orange, gray, fuelGauge and "YES" or "NO ") --# LC Fuel state
  732.     else
  733.       local fuelPercent = math.floor(fuelGauge / SG_MAXFUEL * 100)
  734.       drawElement(9, 3, 1, 1, fuelPercent > 5 and lime or orange, gray, tostring(fuelPercent))  --# SG Fuel level
  735.       term.setTextColor(silver)
  736.       term.write("%  ")
  737.     end
  738.     local allStatusColors = { ["Connected"] = orange, ["Disconnected"] = lime }
  739.     local clearStatusColors = { ["Dialing"] = cyan, ["Disconnecting"] = sky }
  740.     local tgColor = red
  741.     for state, color in pairs(allStatusColors) do
  742.       if gateStatus == state then tgColor = color break end
  743.     end
  744.     if secureStatus == "allclear" then
  745.       for state, color in pairs(clearStatusColors) do
  746.         if gateStatus == state then tgColor = color break end
  747.       end
  748.     end
  749.     term.setTextColor(tgColor)
  750.     term.setCursorPos(26, 3)
  751.     if (gateStatus == "Connected" or gateStatus == "Dialing") and dialAddress == "none" then    --# incoming calls
  752.       if incomingAddress then
  753.         if #incomingAddress > 0 then
  754.           term.write("Incoming from ")
  755.           term.setTextColor(yellow)
  756.           if lcGate then
  757.             term.write(incomingAddress)
  758.           else
  759.             term.write(incomingAddress:sub(1, chevronNumber))
  760.             term.setTextColor(silver)
  761.             term.write(incomingAddress:sub(chevronNumber + 1, #incomingAddress))
  762.           end
  763.         end
  764.       else
  765.         term.write(gateStatus)
  766.       end
  767.     else
  768.       if gateStatus == "Connected" and dialAddress ~= "none" then --# outgoing calls and connection data
  769.         term.write("Connected to ")
  770.         term.setTextColor(yellow)
  771.         term.write(dialAddress)
  772.       else
  773.         term.write(gateStatus)
  774.         term.setCursorPos(34, 3)
  775.       end
  776.       if gateStatus == "Dialing" and chevronNumber <= 9 then      --# dialing data
  777.         term.setTextColor(yellow)
  778.         term.write(dialAddress:sub(1, chevronNumber))
  779.         term.setTextColor(silver)
  780.         term.write(dialAddress:sub(chevronNumber + 1, #dialAddress))
  781.       end
  782.     end
  783.   end
  784. end
  785.  
  786. local function drawControlUI()
  787.   local iColor
  788.   if waitingForIris then iColor = irisState and orange or green end
  789.   drawElement(42, 6, 10, 11, nil, gray, "")                                     --# Control UI menu body
  790.   if runState == "Dial" or runState == "goPage" or runState == "importExport" or runState == "exodus" then
  791.     drawElement(43, 7, 1, 1, waitingForIris and iColor or white, sky, "Iris   ") --# Iris
  792.     drawElement(termX - 1, 7, 1, 1, nil, irisState and green or orange, "  ")   --# Iris pip
  793.     drawElement(43, 9, 1, 1, black, orange, "END Call ")                        --# endCall
  794.     drawElement(43, 11, 1, 1, orange, red, "LOCKDOWN ")                         --# Lockdown
  795.     drawElement(43, 13, 1, 1, gateChange and lime or white, blue, "AddrBook ")  --# Address Book
  796.     drawElement(43, 15, 1, 1, white, green, "New Gate ")                        --# New Gate
  797.   elseif runState == "Trim" then
  798.     drawElement(43, 7, 9, 1, white, orange, " T R I M ")                        --# TRIM MODE
  799.     drawElement(43, 8, 9, 1, nil, nil, " M O D E ")
  800.     drawElement(43, 11, 9, 1, gateChange and white or gray, sky, "Reload   ")   --# Undo
  801.     drawElement(43, 13, 9, 1, nil, green, "Save     ")                          --# Commit
  802.     drawElement(43, 15, 9, 1, white, red, "Close    ")                          --# Exit Trim Mode
  803.   end
  804. end
  805.  
  806. local function drawNaviUI()
  807.   pNum = tostring(gatePage) .. " of " .. tostring(gatePages)
  808.   drawElement(((termX / 2) - 3) - (#pNum / 2) - 2, termY, #pNum, 1, silver, black, " " .. pNum .. " ")
  809.   drawElement((termX / 2) - 16, termY, 7, 1, gray, nil, gatePage > 1 and "<< <" or "    ")
  810.   drawElement((termX / 2) + 2, termY, 7, 1, nil, nil, gatePage < gatePages and "> >>" or "    ")
  811. end
  812.  
  813. local function drawAddressBook()  --# Gate Address Book (Dial / Trim)
  814.   local magicNumber = ((gatePage - 1) * 23) + gatePage
  815.   local xPos, yPos = 2, 6
  816.   for i = magicNumber, #addressBook do
  817.     if runState == "Dial" or runState == "goPage" or runState == "importExport" or runState == "exodus" then
  818.       term.setBackgroundColor(assignColor(i))
  819.       if ((dialAddress == addressBook[i].addr or (#dialAddress == 7 and dialAddress == addressBook[i].addr:sub(1, 7) and addressBook[i].addr:sub(8, 9) == thisGate:sub(8, 9)) or (#addressBook[i].addr == 7 and addressBook[i].addr == dialAddress:sub(1, 7) and dialAddress:sub(8, 9) == thisGate:sub(8, 9))) or (incomingAddress == addressBook[i].addr or (#tostring(incomingAddress) == 7 and incomingAddress == addressBook[i].addr:sub(1, 7) and addressBook[i].addr:sub(8, 9) == thisGate:sub(8, 9)) or (#addressBook[i].addr == 7 and addressBook[i].addr == tostring(incomingAddress):sub(1, 7) and tostring(incomingAddress):sub(8, 9) == thisGate:sub(8, 9)))) and ((#addressBook[i].addr == 9 and thisGate ~= addressBook[i].addr) or (#addressBook[i].addr == 7 and thisGate:sub(1, 7) ~= addressBook[i].addr)) then --# Highlight the gate being dialed
  820.         term.setTextColor(black)  --# currently dialing/conntected to this gate
  821.       else
  822.         term.setTextColor(white)  --# not dialing/connected to this gate
  823.       end
  824.       if (thisGate == addressBook[i].addr or addressBook[i].addr == thisGate:sub(1, 7)) and dhdSettings.highlight then --# Highlight this gate / Dial
  825.         term.setTextColor(silver) --# thisGate
  826.         term.setBackgroundColor(gray)
  827.       end
  828.     elseif runState == "Trim" or runState == "goTrimPage" then
  829.       term.setBackgroundColor(black)
  830.       if ((#addressBook[i].addr == 9 and thisGate == addressBook[i].addr) or (#addressBook[i].addr == 7 and addressBook[i].addr == thisGate:sub(1, 7))) and dhdSettings.highlight then --# Highlight this gate / Trim
  831.         term.setTextColor(gray)   --# thisGate
  832.       else
  833.         term.setTextColor(assignColor(i))
  834.       end
  835.     end
  836.     term.setCursorPos(xPos, yPos)
  837.     term.write(string.rep(" ", math.floor((9 - #addressBook[i].name) / 2)) .. addressBook[i].name .. string.rep(" ", math.ceil((9 - #addressBook[i].name) / 2))) --# Gate Name
  838.     yPos = yPos + 2
  839.     if yPos > 16 then yPos = 6 xPos = xPos + 10 end
  840.     if xPos > 32 then break end
  841.   end
  842. end
  843.  
  844. local function drawHelpUI()
  845.   clearScreen()                                                    --# Body
  846.   drawElement(1, 1, termX, 1, nil, cyan, "")                       --# Header
  847.   drawElement((termX / 2) - 4, 1, 12, 1, cyan, gray, "ccDHD Help") --# Header text
  848.   drawElement(1, termY, termX, 1, nil, nil, "")                    --# Footer
  849.   local lowLine = "ccDHD ver. " .. ccDHDVer .. string.rep(" ", 5) .. "cc# " .. os.getComputerID()
  850.   drawElement(2, termY, 1, 1, silver, nil, lowLine)                --# Footer text
  851.   local tmpLabel = os.getComputerLabel()
  852.   drawElement(termX - #tmpLabel, termY, 1, 1, nil, nil, tmpLabel)
  853.   drawElement(1, 2, termX, 1, nil, nil, "")                        --# Header/Separator
  854.   drawElement(termX - 6, 2, 7, 1, red, silver, " Close ")          --# Close button
  855.   drawElement(1, 3, 14, termY - 3, nil, white, "")                 --# draw help left pane
  856.   drawElement(2, 5, 1, 1, black, nil, "Main Screen")
  857.   drawElement(2, 10, 1, 1, nil, nil, "AddrBook Btn")
  858.   drawElement(2, 14, 1, 1, nil, nil, "Log Screen")
  859.   drawElement(2, 6, 1, 1, gray, nil, "LEFT click")
  860.   drawElement(2, 7, 1, 1, nil, nil, "RIGHT click")
  861.   drawElement(2, 8, 1, 1, nil, nil, "MIDDLE click")
  862.   drawElement(2, 11, 1, 1, nil, nil, "LEFT click")
  863.   drawElement(2, 12, 1, 1, nil, nil, "RIGHT click")
  864.   drawElement(2, 15, 1, 1, nil, nil, "LEFT click")
  865.   drawElement(2, 16, 1, 1, nil, nil, "RIGHT click")
  866.   drawElement(16, 6, 1, 1, silver, black, "an address to dial")
  867.   drawElement(16, 7, 1, 1, nil, nil, "an address to view or edit details")
  868.   drawElement(16, 8, 1, 1, nil, nil, "an address to enter TRIM mode")
  869.   drawElement(16, 11, 1, 1, nil, nil, "to import/export/save address book")
  870.   drawElement(16, 12, 1, 1, nil, nil, "to quick-save address book")
  871.   drawElement(16, 15, 1, 1, nil, nil, "an address to dial")
  872.   drawElement(16, 16, 1, 1, nil, nil, "an address to add to address book")
  873. end
  874.  
  875. local function drawSettingsSwitches()
  876.   --# Settings 1st column
  877.   if hardware.bio > 0 then
  878.     drawElement(22, 10, dhdSettings.bio.lock)      --# Fistprint Authentication (Bioscanner)
  879.     drawElement(2, 12, 1, 1, gray, black, "Bio: ") --# Biolock iris/lockdown function (this is separate from BioLogin)
  880.     if dhdSettings.bio.func == "none" then
  881.       drawElement(7, 12, 1, 1, silver, nil, "No Function")
  882.       drawElement(22, 12, "MID")
  883.     elseif dhdSettings.bio.func == "iris" then
  884.       drawElement(7, 12, 1, 1, green, nil, "Iris       ")
  885.       drawElement(22, 12, true)
  886.     elseif dhdSettings.bio.func == "lock" then
  887.       drawElement(7, 12, 1, 1, red, nil, "Lockdown   ")
  888.       drawElement(22, 12, false)
  889.     end
  890.     drawElement(19 + dhdSettings.bio.auth, 14, 1, 1, black, silver, tostring(dhdSettings.bio.auth))
  891.     drawElement(19 + dhdSettings.bio.fAuth, 16, 1, 1, nil, nil, tostring(dhdSettings.bio.fAuth))
  892.   else
  893.     drawElement(2, 10, 24, 7, gray, black, "No Biolock present")
  894.   end
  895.   --# Settings 2nd column
  896.   drawElement(46, 6, dhdSettings.pSync)      --# pSync
  897.   drawElement(46, 10, dhdSettings.highlight) --# highlight gate
  898.   drawElement(46, 12, dhdSettings.logs)      --# Call logging
  899.   drawElement(46, 14, dhdSettings.startIris) --# Startup Iris state
  900.   drawElement(46, 16, dhdSettings.ecIris)    --# endCall Iris state
  901. end
  902.  
  903. do
  904.   local labels = {
  905.                    "Lockdown pass",
  906.                    "Biolock Login",
  907.                    "Bio",
  908.                    "Bio Login Lvl",
  909.                    "Bio Func. Lvl",
  910.                    "pSync (ccDialer)",
  911.                    "Highlight Gate",
  912.                    "Call Logging",
  913.                    "Incoming Iris",
  914.                    "END Call Iris",
  915.                  }
  916.  
  917.   drawSettingsUI = function()
  918.     drawElement(1, 1, termX, 1, nil, sky, "")                               --# Header
  919.     drawElement(1, 2, termX, 3, nil, gray, "")                              --# Sub-Header
  920.     drawElement(1, 8, termX, 1, nil, nil, "")                               --# Separator
  921.     drawElement(19, 1, 16, 1, sky, nil, "ccDHD Settings")                   --# Title
  922.     drawElement(1, termY, termX, 1, silver, nil, "ccDHD " .. ccDHDVer)      --# Footer / Lowline
  923.     drawElement(17, 3, 1, 1, nil, nil, "This Gate:")                        --# Header (This Gate)
  924.     drawElement(28, 3, 1, 1, cyan, nil, thisGate)                           --# Header (thisGate)
  925.     drawElement(45, 2, 7, 1, red, silver, " Close ")                        --# Close button
  926.     drawElement(45, 4, 6, 1, configChange and lime or gray, nil, " Save  ") --# Save button
  927.     local xOffset, yOffset = 2, 6
  928.     term.setTextColor(gray)
  929.     term.setBackgroundColor(black)
  930.     for i = 1, #labels do
  931.       drawElement(xOffset, yOffset, 1, 1, nil, nil, labels[i])
  932.       yOffset = yOffset + 2
  933.       if yOffset == 8 then yOffset = 10 end
  934.       if yOffset == 18 and xOffset == 2 then
  935.         xOffset = 28
  936.         yOffset = 6
  937.       end
  938.     end
  939.     drawElement(17, 6, 1, 1, orange, nil, "*********") --# password
  940.     if hardware.bio > 0 then
  941.       drawElement(19, 14, 1, 1, gray, nil, "|-----|")  --# Biolock login level
  942.       drawElement(19, 16, 1, 1, nil, nil, "|-----|")   --# Biolock iris/lockdown level
  943.     end
  944.     drawSettingsSwitches()
  945.   end
  946. end
  947.  
  948. local function drawSecureUI()
  949.   if hardware.listMon > 0 then
  950.     for i = 1, hardware.listMon do
  951.       listMon[i].setBackgroundColor(black)
  952.       listMon[i].setTextColor(red)
  953.       listMon[i].setTextScale(0.5)
  954.       listMon[i].clear()
  955.       listMon[i].setCursorPos(1, 5)
  956.       listMon[i].write("!! LOCKDOWN !!")
  957.     end
  958.   end
  959.   if hardware.marquee > 0 then
  960.     if incomingAddress then
  961.       displayStatus()
  962.     else
  963.       for i = 1, hardware.marquee do
  964.         marquee[i].setBackgroundColor(black)
  965.         marquee[i].setTextColor(red)
  966.         marquee[i].setTextScale(2)
  967.         marquee[i].clear()
  968.         marquee[i].setCursorPos(1, 1)
  969.         marquee[i].write("!! LOCKDOWN !!")
  970.       end
  971.     end
  972.   end
  973.   clearLowerScreen()
  974.   drawHeader()
  975.   drawElement((termX / 2) - 7, 9, 1, 1, red, black, "!! LOCKDOWN !!")
  976.   drawElement((termX / 2) - 10, 15, 1, 1, gray, nil, "password:")
  977. end
  978.  
  979. local function drawPopUp()
  980.   if runState == "clearLogs" then
  981.     drawElement((termX / 2) - 5, math.floor(termY / 2) - 2, 13, 1, black, yellow, "Clear Logs?")
  982.     drawElement((termX / 2) - 5, math.floor(termY / 2) - 1, 13, 3, nil, gray, "")
  983.     drawElement((termX / 2) - 4, math.floor(termY / 2), 5, 1, nil, green, "YES")
  984.     drawElement((termX / 2) + 2, math.floor(termY / 2), 5, 1, nil, red, "N O")
  985.   elseif runState == "goPage" or runState == "goTrimPage" then
  986.     drawElement((termX / 2) - 8, termY - 3, 8, 1, white, gray, " :Page: ")
  987.     drawElement((termX / 2) - 8, termY - 2, 8, 2, nil, nil, "")
  988.     drawElement((termX / 2) - 7, termY - 2, 6, 1, nil, black, "") --# input area bg
  989.   elseif runState == "goLogPage" then
  990.     drawElement((termX / 2) - 4, termY - 3, 8, 1, white, gray, " :Page: ")
  991.     drawElement((termX / 2) - 4, termY - 2, 8, 2, nil, nil, "")
  992.     drawElement((termX / 2) - 3, termY - 2, 6, 1, nil, black, "") --# input area bg
  993.   elseif runState == "importExport" then
  994.     drawElement((termX / 2) - 13, math.floor(termY / 2) - 1, 19, 1, white, blue, "Address Book Mgt.")
  995.     drawElement((termX / 2) - 13, math.floor(termY / 2), 19, 7, nil, gray, "")
  996.     if fs.exists("/disk/data/DHDgates") then
  997.       drawElement((termX / 2) - 12, math.floor(termY / 2) + 1, 8, 1, black, yellow, "Import")
  998.       drawElement((termX / 2) - 12, math.floor(termY / 2) + 3, 8, 1, white, purple, "Merge!")
  999.     else
  1000.       drawElement((termX / 2) - 12, math.floor(termY / 2) + 1, 8, 1, gray, silver, "Import")
  1001.       drawElement((termX / 2) - 12, math.floor(termY / 2) + 3, 8, 1, nil, nil, "Merge!")
  1002.     end
  1003.     if fs.exists("/disk") then
  1004.       drawElement((termX / 2) - 3, math.floor(termY / 2) + 1, 8, 1, black, orange, "Export")
  1005.     else
  1006.       drawElement((termX / 2) - 3, math.floor(termY / 2) + 1, 8, 1, gray, silver, "Export")
  1007.     end
  1008.     if gateChange then
  1009.       drawElement((termX / 2) - 3, math.floor(termY / 2) + 3, 8, 1, white, green, "Save")
  1010.     else
  1011.       drawElement((termX / 2) - 3, math.floor(termY / 2) + 3, 8, 1, gray, silver, "Save")
  1012.     end
  1013.     drawElement((termX / 2) - 7, math.floor(termY / 2) + 5, 7, 1, white, red, "CLOSE")
  1014.   elseif runState == "newPassword" then
  1015.     drawElement((termX / 2) - 6, 6, 14, 1, white, blue, "Lockdown P/W")
  1016.     drawElement((termX / 2) - 6, 7, 14, 5, nil, silver, "") --# body
  1017.     drawElement((termX / 2) - 6, 7, 14, 1, black, nil, " OLD Password ")
  1018.     drawElement((termX / 2) - 6, 9, 14, 1, nil, nil, " NEW Password ")
  1019.     drawElement((termX / 2) - 5, 8, 12, 1, nil, black, "")  --# oldPass input area bg
  1020.     drawElement((termX / 2) - 5, 10, 12, 1, nil, nil, "")   --# newPass input area bg
  1021.     drawElement((termX / 2) - 3, 11, 8, 1, white, orange, "Cancel")
  1022.   elseif runState == "exodus" then
  1023.     if gateChange and not configChange then
  1024.       drawElement((termX / 2) - 13, math.floor(termY / 2) - 1, 19, 1, white, blue, "Save Addr Book?")
  1025.     elseif configChange and not gateChange then
  1026.       drawElement((termX / 2) - 13, math.floor(termY / 2) - 1, 19, 1, white, blue, "Save Settings?")
  1027.     else
  1028.       drawElement((termX / 2) - 13, math.floor(termY / 2) - 1, 19, 1, white, blue, "Save Gates & Cfg?")
  1029.     end
  1030.     drawElement((termX / 2) - 13, math.floor(termY / 2), 19, 3, nil, gray, "")
  1031.     drawElement((termX / 2) - 12, math.floor(termY / 2) + 1, 8, 1, nil, green, "Save")
  1032.     drawElement((termX / 2) - 3, math.floor(termY / 2) + 1, 8, 1, nil, orange, "Quit")
  1033.   end
  1034. end
  1035.  
  1036. local function drawMenu()
  1037.   menuState = true
  1038.   drawElement(2, 1, 5, 1, white, black, "_DHD_")
  1039.   drawElement(2, 2, 1, 9, nil, gray, "")   --# dark gray line along left side of menu
  1040.   drawElement(2, 3, 1, 1, nil, sky, "")    --# Settings pip
  1041.   drawElement(2, 5, 1, 1, nil, yellow, "") --# Logs pip
  1042.   drawElement(2, 7, 1, 1, nil, cyan, "")   --# Help pip
  1043.   drawElement(2, 9, 1, 1, nil, red, "")    --# Exit pip
  1044.   drawElement(3, 2, 9, 9, nil, silver, "") --# menu body
  1045.   drawElement(3, 3, 1, 1, gray, nil, "Settings")
  1046.   drawElement(3, 5, 1, 1, nil, nil, "View Logs")
  1047.   drawElement(3, 7, 9, 1, nil, nil, "DHD Help")
  1048.   drawElement(3, 9, 1, 1, nil, nil, "  EXIT  ")
  1049. end
  1050.  
  1051. do
  1052.   local ratings = {
  1053.     [1] = { rating = "B", color = blue, desc = "Base/Outpost/Hub" },
  1054.     [2] = { rating = "H", color = sky, desc = "Home/Camp" },
  1055.     [3] = { rating = "V", color = brown, desc = "Village" },
  1056.     [4] = { rating = "M", color = purple, desc = "Misc/Special" },
  1057.     [5] = { rating = "S", color = green, desc = "Safe/Secured" },
  1058.     [6] = { rating = "C", color = orange, desc = "Caution" },
  1059.     [7] = { rating = "D", color = red, desc = "Danger" },
  1060.     [8] = { rating = "U", color = silver, desc = "Unk/Unclassified" },
  1061.   }
  1062.  
  1063.   drawRatingList = function(gRating)
  1064.     local rColor
  1065.     drawElement(18, 6, 20, 1, white, brown, "Classification")
  1066.     drawElement(18, 7, 20, 10, nil, gray, "")                 --# menu body
  1067.     for i = 1, #ratings do
  1068.       if gRating == ratings[i].rating then
  1069.         rColor = gRating == "U" and white or ratings[i].color
  1070.         drawElement(37, i + 7, 1, 1, nil, ratings[i].color, "") --# selected rating color pip
  1071.       else
  1072.         rColor = silver
  1073.       end
  1074.       drawElement(18, i + 7, 1, 1, nil, ratings[i].color, "") --# color pip
  1075.       drawElement(20, i + 7, 1, 1, rColor, gray, ratings[i].desc)
  1076.     end
  1077.   end
  1078. end
  1079.  
  1080. drawCLI = function(_mon)
  1081.   --# Terminal output
  1082.   if menuState then
  1083.     if redraw or incomingAddress or dialAddress ~= "none" then
  1084.       drawHeader()
  1085.       drawAddressBook()
  1086.       if gateChange then drawControlUI() end
  1087.       drawNaviUI()
  1088.     end
  1089.     drawMenu()
  1090.   else
  1091.     if runState ~= "DHDsettings" and runState ~= "newPassword" and runState ~= "logs" and runState ~= "clearLogs" then
  1092.       drawHeader()
  1093.     end
  1094.   end
  1095.   redraw = false
  1096.   if not menuState then
  1097.     if runState == "goPage" or runState == "goTrimPage" or runState == "importExport" or runState == "exodus" then
  1098.       drawAddressBook()
  1099.       drawControlUI()
  1100.       drawPopUp()
  1101.     elseif runState == "Dial" or runState == "Trim" then
  1102.       if secureStatus == "lockdown" then
  1103.         drawSecureUI()
  1104.       else
  1105.         drawAddressBook()
  1106.         drawControlUI()
  1107.         drawNaviUI()
  1108.       end
  1109.     elseif runState == "DHDhelp" then
  1110.       drawHelpUI()
  1111.     end
  1112.     if gettingInput then redrawInput() end
  1113.   end
  1114.   --# Monitor output
  1115.   if _mon then
  1116.     if displayState == "list" and secureStatus == "allclear" then
  1117.       if hardware.marquee > 0 then
  1118.         displayStatus()
  1119.       end
  1120.       if hardware.listMon > 0 then
  1121.         displayAddressBook()
  1122.       end
  1123.     elseif displayState == "info" and secureStatus == "allclear" then
  1124.       if hardware.listMon > 0 and hardware.marquee > 0 then
  1125.         displayNotes(selectedGate)
  1126.       end
  1127.     end
  1128.   end
  1129. end
  1130.  
  1131. local function clearCallHistory()
  1132.   for i = #callHistory, 1, -1 do
  1133.     callHistory[i] = nil
  1134.   end
  1135. end
  1136.  
  1137. local function drawLogScreen()
  1138.   if logPage == logPages and logPages > 1 then
  1139.     clearLowerScreen()
  1140.   end
  1141.   drawElement(1, termY, termX, 1, nil, gray, "")  --# Footer
  1142.   if not callHistory[1] then                      --# No logs to display
  1143.     drawElement(2, 6, 1, 1, gray, black, "No Logs")
  1144.   else                                            --# Logs to display
  1145.     --# populate footer
  1146.     drawElement(11, termY, 7, 1, silver, nil, "<< <")
  1147.     term.setCursorPos(math.floor(termX / 2) - (math.floor((#tostring(logPage) + #tostring(logPages)) / 2) + 2), termY)
  1148.     term.write(tostring(logPage) .. " of " .. tostring(logPages) .. "  ")
  1149.     drawElement(36, termY, 1, 1, nil, nil, "> >>")
  1150.     --# end of footer
  1151.     term.setBackgroundColor(black)
  1152.     local currentEntry = ((logPage - 1) * 11) + 1 --# Set the first entry to show (based on page number)
  1153.     local spacer = string.rep(" ", 9)
  1154.     for i = currentEntry, currentEntry + 10 do    --# Display logs
  1155.       local callDir = callHistory[i]:sub(string.find(callHistory[i], "<") + 1, string.find(callHistory[i], ">") - 1)
  1156.       term.setTextColor(callDir == "Inbound" and gray or silver)
  1157.       term.setCursorPos(2, i - currentEntry + 6)  --# position entry
  1158.       term.write(callHistory[i]:sub(1, string.find(callHistory[i], "@") - 2) .. " ") --# write day
  1159.       term.setCursorPos(9, i - currentEntry + 6)  --# position entry
  1160.       term.write(callHistory[i]:sub(string.find(callHistory[i], "@") + 2, string.find(callHistory[i], "M"))) --# write time
  1161.       term.setCursorPos(20, i - currentEntry + 6) --# position entry
  1162.       term.write(callDir .. " ")                  --# write direction
  1163.       term.setCursorPos(31, i - currentEntry + 6) --# position entry
  1164.       local callAddr = callHistory[i]:sub(string.find(callHistory[i], ">") + 2)      --# set address
  1165.       term.setTextColor(callDir == "Inbound" and cyan or sky)
  1166.       term.write(callAddr .. spacer)              --# write address (and clear space for truncated entries)
  1167.       if i >= #callHistory then break end         --# if we've reached the end of the log then stop the loop
  1168.     end
  1169.   end
  1170. end
  1171.  
  1172. local function drawGateData()
  1173.   if addressBook[currentEdit].addr == thisGate or (not lcGate and addressBook[currentEdit].addr == thisGate:sub(1, 7)) then
  1174.     drawElement((termX / 2), 8, 1, 1, gray, black, "< This Gate >")
  1175.   else
  1176.     drawElement((termX / 2), 8, 1, 1, nil, black, "             ")
  1177.   end
  1178.   local xStr, yStr, zStr, ceStr = tostring(addressBook[currentEdit].loc.x), tostring(addressBook[currentEdit].loc.y), tostring(addressBook[currentEdit].loc.z), tostring(currentEdit)
  1179.   drawElement(11, 6, 1, 1, yellow, black, addressBook[currentEdit].name .. string.rep(" ", 9 - #addressBook[currentEdit].name))    --# Name
  1180.   drawElement(13, 14, 1, 1, nil, nil, addressBook[currentEdit].loc.dim .. string.rep(" ", 19 - #addressBook[currentEdit].loc.dim)) --# Dimension
  1181.   drawElement(11, 8, 1, 1, assignColor(currentEdit), nil, addressBook[currentEdit].addr .. "  ")                                   --# Address
  1182.   drawElement(18, 12, 1, 1, nil, nil, assignRating(currentEdit) .. string.rep(" ", 20 - #assignRating(currentEdit)))               --# Classification
  1183.   drawElement(termX - 6, 6, 1, 1, white, nil, ceStr .. string.rep(" ", 5 - #ceStr))                                                --# Position in address book
  1184.   drawElement(8, 10, 1, 1, nil, nil, addressBook[currentEdit].note:sub(1, 43) .. string.rep(" ", 43 - #addressBook[currentEdit].note:sub(1, 43))) --# Note
  1185.   drawElement(15, 16, 1, 1, silver, nil, xStr .. string.rep(" ", 9 - #xStr))                                                       --# X
  1186.   drawElement(28, 16, 1, 1, nil, nil, yStr .. string.rep(" ", 9 - #yStr))                                                          --# Y
  1187.   drawElement(41, 16, 1, 1, nil, nil, zStr .. string.rep(" ", 9 - #zStr))                                                          --# Z
  1188.   if addressBook[currentEdit].iris == "none" then                                                                                  --# Iris control
  1189.     drawElement(31, 6, "mid")
  1190.   elseif addressBook[currentEdit].iris == "open" then
  1191.     drawElement(31, 6, true)
  1192.   elseif addressBook[currentEdit].iris == "close" then
  1193.     drawElement(31, 6, false)
  1194.   end
  1195. end
  1196.  
  1197. local function drawGateLabels()
  1198.   clearLowerScreen()
  1199.   drawHeader()
  1200.   drawElement(2, 6, 1, 1, gray, black, "Name:                  Iris:             #")
  1201.   drawElement(2, 8, 1, 1, nil, nil, "Address:")
  1202.   drawElement(2, 10, 1, 1, nil, nil, "Note:")
  1203.   drawElement(2, 12, 1, 1, nil, nil, "Classification:")
  1204.   drawElement(2, 14, 10, 1, nil, nil, "Dimension:")
  1205.   drawElement(2, 16, 1, 1, nil, nil, "Coords:   x:           y:           z:")
  1206.   drawElement((termX / 2) - 8, termY - 1, 6, 1, gateChange and green or silver, gray, " Save ")
  1207.   drawElement((termX / 2) + 1, termY - 1, 7, 1, red, nil, " Close ")
  1208.   drawGateData()
  1209. end
  1210.  
  1211. local function addNewAddress(newAddress, fast)
  1212.   if #addressBook < 23976 then                       --# if the address book isn't yet full...
  1213.     addressBook[#addressBook + 1] = { name = newAddress or "NEW GATE", addr = newAddress or "ADDRESS", rating = "U", iris = "none", note = newAddress and "Added from logs" or "short note", loc = { x = 99999, y = 99999, z = 99999, dim = "Overworld", }, } --# ...create new entry
  1214.     paginateGates()                                  --# paginate the address book
  1215.     gateChange = true                                --# indicate the address book has changed
  1216.     if fast then                                     --# if it's a quick-add...
  1217.       if gatePage == gatePages then drawAddressBook() end --# ...redraw the address book if we're on the last page
  1218.       drawNaviUI()                                   --# update the page navigation UI (page number)
  1219.       return
  1220.     end
  1221.     currentEdit = #addressBook                       --# set the entry number being edited
  1222.     drawGateLabels()
  1223.   else
  1224.     runState = "Dial"
  1225.   end
  1226. end
  1227.  
  1228. local function flashDial(gate, gX, gY, gateNum)
  1229.   local gColor = assignColor(gateNum)
  1230.   local gLabel = string.rep(" ", math.floor((9 - #gate) / 2)) .. gate .. string.rep(" ", math.ceil((9 - #gate) / 2))
  1231.   drawElement(gX, gY, 9, 1, gColor, black, gLabel)
  1232.   sleep(0.1)
  1233.   drawElement(gX, gY, 9, 1, black, gColor, gLabel)
  1234. end
  1235.  
  1236. local function flashChoice(x, y, tCo, bCo, label)
  1237.   drawElement(x, y, 1, 1, tCo, bCo, label)
  1238.   sleep(0.1)
  1239. end
  1240.  
  1241. local function clearLogsPopUp()
  1242.   drawPopUp()
  1243.   local _, mButton, mX, mY = os.pullEvent("mouse_click")
  1244.   if mY == math.floor(termY / 2) then
  1245.     if mX > termX / 2 - 5 and mX < termX / 2 and mButton == 1 then
  1246.       flashChoice((termX / 2) - 4, math.floor(termY / 2), green, white, " YES ")
  1247.       local clearLog = fs.open(gateHistory, "w")
  1248.       clearLog.close()
  1249.       clearCallHistory()
  1250.       logPage, logPages = 1, 1
  1251.       runState = "logs"
  1252.       clearLowerScreen()
  1253.       drawLogScreen()
  1254.       return
  1255.     elseif mX > termX / 2 and mX < termX / 2 + 6 and mButton == 1 then
  1256.       flashChoice((termX / 2) + 2, math.floor(termY / 2), red, white, " N O ")
  1257.       drawElement(termX / 2 - 5, math.floor(termY / 2) - 2, 13, 4, nil, black, "") --# clear dialogue box
  1258.       runState = "logs"
  1259.       drawLogScreen()
  1260.       return
  1261.     end
  1262.   end
  1263. end
  1264.  
  1265. local function saveAndQuitPopUp()
  1266.   local _, mButton, mX, mY = os.pullEvent("mouse_click")
  1267.   if mX > (termX / 2) - 14 and mX < (termX / 2) + 5 and mY > math.floor(termY / 2) - 2 and mY < math.floor(termY / 2) + 3 then
  1268.     if mX > (termX / 2) - 13 and mX < (termX / 2) - 5 and mY == math.floor(termY / 2) + 1 and mButton == 1 then
  1269.       flashChoice((termX / 2) - 12, math.floor(termY / 2) + 1, green, white, "  Save  ")
  1270.       if gateChange then
  1271.         saveData(gateData, "gate")
  1272.       end
  1273.       if configChange then
  1274.         saveData(settingsData, "cfg")
  1275.       end
  1276.       kernelState = false
  1277.       return true
  1278.     elseif mX > (termX / 2) - 4 and mX < (termX / 2) + 4 and mY == math.floor(termY / 2) + 1 and mButton == 1 then
  1279.       flashChoice((termX / 2) - 3, math.floor(termY / 2) + 1, orange, white, "  Quit  ")
  1280.       kernelState = false
  1281.       return true
  1282.     end
  1283.   else
  1284.     runState = "Dial"
  1285.     drawElement(2, 6, 39, 11, nil, black, "")
  1286.     drawAddressBook()
  1287.     return false
  1288.   end
  1289. end
  1290.  
  1291. local function importExportPopUp()
  1292.   while true do
  1293.     local _, mButton, mX, mY = os.pullEvent("mouse_click")
  1294.     if mX > (termX / 2) - 14 and mX < (termX / 2) + 5 and mY < math.floor(termY / 2) + 7 and mY > math.floor(termY / 2) - 2 then
  1295.       if mX > (termX / 2) - 13 and mX < termX / 2 - 5 and mY == math.floor(termY / 2) + 1 and mButton == 1 then
  1296.         if fs.exists("/disk/data/DHDgates") then
  1297.           flashChoice((termX / 2) - 12, math.floor(termY / 2) + 1, yellow, black, " Import ")
  1298.           ingestData("/disk/data/DHDgates", "gate")
  1299.           drawElement(2, 6, 39, 11, nil, nil, "")
  1300.           drawAddressBook()
  1301.           drawNaviUI()
  1302.           if displayState == "info" then displayState = "list" end
  1303.           displayAddressBook()
  1304.           displayMarquee()
  1305.           drawElement(43, 13, 1, 1, lime, blue, "AddrBook ")
  1306.           drawPopUp()
  1307.         end
  1308.       elseif mX > termX / 2 - 4 and mX < (termX / 2) + 4 and mY == math.floor(termY / 2) + 1 and mButton == 1 then
  1309.         if fs.exists("/disk") then
  1310.           flashChoice((termX / 2) - 3, math.floor(termY / 2) + 1, orange, black, " Export ")
  1311.           saveData("/disk/data/DHDgates", "gate")
  1312.           drawPopUp()
  1313.         end
  1314.       elseif mX > (termX / 2) - 13 and mX < (termX / 2) - 5 and mY == math.floor(termY / 2) + 3 and mButton == 1 then
  1315.         if fs.exists("/disk/data/DHDgates") then
  1316.           flashChoice((termX / 2) - 12, math.floor(termY / 2) + 3, purple, white, " Merge! ")
  1317.           mergeData()
  1318.           if gateChange then
  1319.             drawAddressBook()
  1320.             drawNaviUI()
  1321.             if displayState == "list" then displayAddressBook() end
  1322.             drawElement(43, 13, 1, 1, lime, blue, "AddrBook ")
  1323.           end
  1324.           drawPopUp()
  1325.         end
  1326.       elseif mX > (termX / 2) - 4 and mX < termX / 2 + 4 and mY == math.floor(termY / 2) + 3 and mButton == 1 and gateChange then
  1327.         flashChoice((termX / 2) - 3, math.floor(termY / 2) + 3, green, white, "  Save  ")
  1328.         saveData("/data/DHDgates", "gate")
  1329.         drawElement(43, 13, 1, 1, white, blue, "AddrBook ")
  1330.         drawPopUp()
  1331.       elseif mX > (termX / 2) - 8 and mX < (termX / 2) - 1 and mY == math.floor(termY / 2) + 5 and mButton == 1 then
  1332.         runState = "Dial"
  1333.         flashChoice((termX / 2) - 7, math.floor(termY / 2) + 5, red, white, " CLOSE ")
  1334.         drawElement(2, 6, 39, 11, nil, black, "")
  1335.         drawAddressBook()
  1336.         return
  1337.       end
  1338.     end
  1339.   end
  1340. end
  1341.  
  1342. local function readInput(curX, curY, cO, bG, _limit, gField, mask) --# cursor X, Y, text color, bg color, character limit, gate address field, character mask
  1343.   gettingInput = true
  1344.   local symbols = { ["A"] = true, ["B"] = true, ["C"] = true, ["D"] = true, ["E"] = true , ["F"] = true, ["G"] = true, ["H"] = true, ["I"] = true, ["J"] = true, ["K"] = true, ["L"] = true, ["M"] = true, ["N"] = true, ["O"] = true, ["P"] = true, ["Q"] = true, ["R"] = true, ["S"] = true, ["T"] = true, ["U"] = true, ["V"] = true, ["W"] = true, ["X"] = true, ["Y"] = true, ["Z"] = true, ["0"] = true, ["1"] = true, ["2"] = true, ["3"] = true, ["4"] = true, ["5"] = true, ["6"] = true, ["7"] = true, ["8"] = true, ["9"] = true, ["-"] = true, ["+"] = true }
  1345.   local passSymbols = { "!", "@", "#", "$", "%", "^", "&", "*", "?", "=", "+", "-", "_", "x", "X" }
  1346.   word = ""
  1347.   secWord = ""
  1348.   bCurX, bCurY = curX, curY
  1349.   local bgColor = bG or black
  1350.   curColor, bColor = cO, bgColor
  1351.   local pos = 0
  1352.   _limit = (_limit and type(_limit) == "number" and _limit > 0) and _limit or 43
  1353.   if type(mask) ~= "string" then mask = nil end
  1354.   term.setTextColor(curColor)
  1355.   term.setBackgroundColor(bgColor)
  1356.   term.setCursorBlink(true)
  1357.   term.setCursorPos(curX, curY)
  1358.   while true do
  1359.     local event, data, mX, mY = os.pullEvent()
  1360.     if event == "key" then
  1361.       if data == keys.backspace then
  1362.         curX = math.max(bCurX, curX - 1)
  1363.         pos = math.max(0, pos - 1)
  1364.         word = #word > 0 and word:sub(1, #word - 1) or ""
  1365.         if secureStatus == "lockdown" then
  1366.           secWord = #secWord > 0 and secWord:sub(1, #secWord - 1) or ""
  1367.         end
  1368.         drawElement(curX, curY, 1, 1, nil, bgColor, " ")
  1369.         term.setCursorPos(curX, curY)
  1370.       elseif data == keys.enter or data == keys.numPadEnter then
  1371.         if secureStatus == "lockdown" then
  1372.           drawElement(bCurX, bCurY, #word, 1, nil, bgColor, "")
  1373.         end
  1374.         break
  1375.       elseif data == keys.tab and runState == "newPassword" then
  1376.         break
  1377.       end
  1378.     elseif event == "char" and pos < _limit then
  1379.       local goodData = false
  1380.       if secureStatus == "lockdown" then
  1381.         local secSymbol = passSymbols[math.random(1, #passSymbols)]
  1382.         secWord = secWord .. secSymbol
  1383.         drawElement(curX, curY, 1, 1, cO, bgColor, secSymbol)
  1384.         goodData = true
  1385.       else
  1386.         if gField then
  1387.           local dUpper = data:upper()
  1388.           if symbols[dUpper] then
  1389.             drawElement(curX, curY, 1, 1, curColor, bgColor, dUpper)
  1390.             goodData = true
  1391.           end
  1392.         else
  1393.           if mask then
  1394.             drawElement(curX, curY, 1, 1, curColor, bgColor, mask)
  1395.           else
  1396.             drawElement(curX, curY, 1, 1, curColor, bgColor, data)
  1397.           end
  1398.           goodData = true
  1399.         end
  1400.       end
  1401.       if goodData then
  1402.         word = word .. data
  1403.         curX = curX + 1
  1404.         pos = pos + 1
  1405.       end
  1406.     elseif event == "paste" and pos < _limit then
  1407.       if pos + #data > _limit then
  1408.         data = data:sub(1, _limit - pos)
  1409.       end
  1410.       if secureStatus == "lockdown" then
  1411.         local secSymbols = { }
  1412.         for i = 1, #data do
  1413.           secSymbols[i] = passSymbols[math.random(1, #passSymbols)]
  1414.         end
  1415.         local newSec = table.concat(secSymbols)
  1416.         secWord = secWord .. newSec
  1417.         drawElement(curX, curY, 1, 1, curColor, bgColor, newSec)
  1418.       elseif gField then
  1419.         local newWord, glyph = { }, ""
  1420.         for i = 1, #data do
  1421.           glyph = string.upper(data:sub(i, i))
  1422.           if symbols[glyph] then
  1423.             newWord[i] = glyph
  1424.           else
  1425.             newWord[i] = "?"
  1426.           end
  1427.         end
  1428.         data = table.concat(newWord)
  1429.         drawElement(curX, curY, 1, 1, curColor, bgColor, data)
  1430.       else
  1431.         if mask then
  1432.           local maskTable = { }
  1433.           for i = 1, #data do
  1434.             maskTable[i] = mask
  1435.           end
  1436.           drawElement(curX, curY, 1, 1, curColor, bgColor, table.concat(maskTable))
  1437.         else
  1438.           drawElement(curX, curY, 1, 1, curColor, bgColor, data)
  1439.         end
  1440.       end
  1441.       word = word .. data
  1442.       curX = curX + #data
  1443.       pos = pos + #data
  1444.     elseif event == "mouse_click" and secureStatus == "allclear" then
  1445.       if runState == "newPassword" then
  1446.         if mY == 11 and mX > (termX / 2) - 4 and mX < (termX / 2) + 4 then
  1447.           term.setCursorBlink(false)
  1448.           gettingInput = false
  1449.           flashChoice((termX / 2) - 3, 11, orange, white, " Cancel ")
  1450.           runState = "DHDsettings"
  1451.           drawElement((termX / 2) - 6, 6, 14, 6, nil, black, "")
  1452.           drawSettingsUI()
  1453.           return
  1454.         elseif mX > (termX / 2) - 6 and mX < (termX / 2) + 6 and ((mY == 8 and bCurY == 10) or (mY == 10 and bCurY == 8)) then
  1455.           break
  1456.         end
  1457.       else
  1458.         if mY ~= curY or mX < bCurX or mX >= bCurX + _limit then
  1459.           break
  1460.         end
  1461.       end
  1462.     elseif event == "mouse_scroll" and runState == "GateEdit" and secureStatus == "allclear" then
  1463.       break
  1464.     end
  1465.   end
  1466.   term.setCursorBlink(false)
  1467.   gettingInput = false
  1468.   return word
  1469. end
  1470.  
  1471. local function goToLogPage()
  1472.   local newPage = tonumber(readInput((termX / 2 - 2), termY - 2, lime, nil, 4))
  1473.   drawElement((termX / 2) - 4, termY - 3, 8, 3, nil, nil, "")
  1474.   logPage = newPage or logPage
  1475.   logPage = math.max(1, logPage)
  1476.   logPage = math.min(logPage, logPages)
  1477.   if logPage == logPages and logPages > 1 then
  1478.     drawElement(2, 6, 39, 11, nil, nil, "")
  1479.   end
  1480.   runState = "logs"
  1481.   drawLogScreen()
  1482. end
  1483.  
  1484. local function goToPage()
  1485.   local newPage = tonumber(readInput((termX / 2 - 6), termY - 2, lime, nil, 4))
  1486.   drawElement((termX / 2) - 8, termY - 3, 8, 3, nil, nil, "")
  1487.   gatePage = newPage or gatePage
  1488.   gatePage = math.max(1, gatePage)
  1489.   gatePage = math.min(gatePage, gatePages)
  1490.   if gatePage == gatePages and gatePages > 1 then
  1491.     drawElement(2, 6, 39, 11, nil, nil, "")
  1492.   end
  1493.   runState = runState == "goPage" and "Dial" or "Trim"
  1494.   drawAddressBook()
  1495.   drawNaviUI()
  1496. end
  1497.  
  1498. local function changeLockdownPassword()
  1499.   drawPopUp()
  1500.   local oldPass, newPass, goodChange = "", "", false
  1501.   while true do
  1502.     drawElement((termX / 2) - 5, 8, 12, 1, nil, black, "") --# old password entry area
  1503.     oldPass = readInput((termX / 2) - 5, 8, yellow, black, 12, nil, "*")
  1504.     if runState == "DHDsettings" then return end
  1505.     if oldPass ~= "" then
  1506.       drawElement((termX / 2) - 5, 8, 1, 1, gray, nil, string.rep("*", #oldPass))
  1507.     end
  1508.     if newPass ~= "" and newPass ~= dhdSettings.password and oldPass == dhdSettings.password then
  1509.       goodChange = true
  1510.       break
  1511.     end
  1512.     drawElement((termX / 2) - 5, 10, 12, 1, nil, nil, "")  --# new password entry area
  1513.     newPass = readInput((termX / 2) - 5, 10, yellow, black, 12, nil, "*")
  1514.     if runState == "DHDsettings" then return end
  1515.     if newPass ~= "" then
  1516.       drawElement((termX / 2) - 5, 10, 1, 1, gray, nil, string.rep("*", #newPass))
  1517.     end
  1518.     if newPass ~= "" and newPass ~= dhdSettings.password and oldPass == dhdSettings.password then
  1519.       goodChange = true
  1520.       break
  1521.     end
  1522.   end
  1523.   if goodChange then
  1524.     configChange = true
  1525.     dhdSettings.password = newPass
  1526.     runState = "DHDsettings"
  1527.     drawElement((termX / 2) - 6, 6, 14, 6, nil, nil, "") --# clear password change box
  1528.     drawSettingsUI()
  1529.   end
  1530. end
  1531.  
  1532. local function deleteGate(gateNum)            --# TRIM mode: delete gate
  1533.   if #addressBook > 1 then                    --# Don't delete if only 1 gate remaining
  1534.     table.remove(addressBook, gateNum)
  1535.     gateChange = true
  1536.     drawElement(43, 11, 9, 1, white, sky, "Reload   ") --# Reload
  1537.     drawElement(43, 13, 9, 1, nil, green, "Save     ") --# Commit
  1538.     paginateGates()
  1539.     gatePage = math.min(gatePage, gatePages)
  1540.     listPage = math.min(listPage, listPages)
  1541.     if gatePage == gatePages then drawElement(2, 6, 39, 11, nil, black, "") end --# Clear gate entry area of screen
  1542.   end
  1543. end
  1544.  
  1545. local function editGate()
  1546.   while true do
  1547.     local _, mButton, mcX, mcY = os.pullEvent("mouse_click")
  1548.     if mcY == 6 and mButton == 1 then
  1549.       if mcX > 10 and mcX < 20 then        --# Gate Name
  1550.         drawElement(11, 6, 1, 1, gray, black, addressBook[currentEdit].name)
  1551.         local newGateName = readInput(11, 6, yellow, black, 9)
  1552.         if newGateName ~= "" and newGateName ~= "nil" then
  1553.           addressBook[currentEdit].name = newGateName
  1554.           if addressBook[currentEdit].addr == "ADDRESS" and (#newGateName == 7 or #newGateName == 9) and not string.find(newGateName, " ") then
  1555.             addressBook[currentEdit].addr = string.upper(newGateName)
  1556.           end
  1557.           gateChange = true
  1558.         end
  1559.         drawElement(11, 6, 1, 1, yellow, black, addressBook[currentEdit].name .. string.rep(" ", 9 - #addressBook[currentEdit].name))
  1560.       elseif mcX > 30 and mcX < 35 then --# Iris control
  1561.         if addressBook[currentEdit].iris == "none" then
  1562.           addressBook[currentEdit].iris = "open"
  1563.           drawElement(31, 6, true)
  1564.         elseif addressBook[currentEdit].iris == "open" then
  1565.           addressBook[currentEdit].iris = "close"
  1566.           drawElement(31, 6, false)
  1567.         elseif addressBook[currentEdit].iris == "close" then
  1568.           addressBook[currentEdit].iris = "none"
  1569.           drawElement(31, 6, "mid")
  1570.         end
  1571.         gateChange = true
  1572.       elseif mcX > termX - 7 and mcX < termX - 6 + #tostring(currentEdit) then --# Address Book entry position (for reordering)
  1573.         drawElement(termX - 6, 6, 1, 1, gray, black, tostring(currentEdit))
  1574.         local oldPos = currentEdit
  1575.         local newPos = math.floor(tonumber(readInput(termX - 6, 6, white, black, 5)))
  1576.         if newPos and newPos > 0 and newPos <= #addressBook then
  1577.           local tempGateData = { }
  1578.           for k, v in pairs(addressBook[currentEdit]) do
  1579.             tempGateData[k] = v
  1580.           end
  1581.           table.remove(addressBook, currentEdit)
  1582.           table.insert(addressBook, newPos, tempGateData)
  1583.           currentEdit = newPos
  1584.           gateChange = true
  1585.         end
  1586.         drawElement(termX - 6, 6, 1, 1, white, black, tostring(currentEdit) .. string.rep(" ", 5 - #tostring(currentEdit)))
  1587.       end
  1588.     elseif mcY == 8 and mButton == 1 then
  1589.       if mcX > 10 and mcX < 20 then        --# Gate Address
  1590.         drawElement(11, 8, 1, 1, gray, black, addressBook[currentEdit].addr)
  1591.         local newGate = string.upper(readInput(11, 8, yellow, black, 9, true))
  1592.         if newGate ~= "" and newGate ~= "NIL" and (#newGate == 7 or #newGate == 9) then
  1593.           addressBook[currentEdit].addr = newGate
  1594.           if addressBook[currentEdit].name == "NEW GATE" or addressBook[currentEdit].name == "Name" then
  1595.             addressBook[currentEdit].name = newGate
  1596.             drawElement(11, 6, 1, 1, yellow, black, addressBook[currentEdit].name .. string.rep(" ", 9 - #addressBook[currentEdit].name))
  1597.           end
  1598.           gateChange = true
  1599.         end
  1600.         drawElement(11, 8, 1, 1, assignColor(currentEdit), black, addressBook[currentEdit].addr .. "  ")
  1601.       end
  1602.     elseif mcY == 10 and mButton == 1 then --# Edit Gate Note
  1603.       if mcX > 7 and mcX < 8 + #addressBook[currentEdit].note:sub(1, 43) then
  1604.         drawElement(8, 10, 1, 1, gray, black, addressBook[currentEdit].note:sub(1, 43))
  1605.         local newNote = tostring(readInput(8, 10, white, black, 43))
  1606.         if newNote ~= "" and newNote ~= "nil" then
  1607.           addressBook[currentEdit].note = newNote
  1608.           gateChange = true
  1609.         end
  1610.         drawElement(8, 10, 1, 1, white, black, addressBook[currentEdit].note:sub(1, 43) .. string.rep(" ", 43 - #addressBook[currentEdit].note:sub(1, 43)))
  1611.       end
  1612.     elseif mcY == 12 and mButton == 1 then --# Edit Gate Rating/Classification
  1613.       if mcX > 17 and mcX < 18 + #assignRating(currentEdit) then
  1614.         drawRatingList(addressBook[currentEdit].rating)
  1615.         local selected = false
  1616.         while true do
  1617.           local _, mcButton, mX, mY = os.pullEvent("mouse_click")
  1618.           if mX > 17 and mX < 38 and mY > 5 and mY < 17 then
  1619.             local gRatings = { "B", "H", "V", "M", "S", "C", "D", "U", }
  1620.             for i = 1, #gRatings do
  1621.               if mY == i + 7 then
  1622.                 if addressBook[currentEdit].rating ~= gRatings[i] then
  1623.                   addressBook[currentEdit].rating = gRatings[i]
  1624.                   gateChange = true
  1625.                 end
  1626.                 selected = true
  1627.                 break
  1628.               end
  1629.             end
  1630.             if selected then break end
  1631.           else
  1632.             break
  1633.           end
  1634.         end
  1635.         drawElement(18, 6, 20, 11, nil, black, "") --# clear rating popup
  1636.         if gateChange then
  1637.           drawElement((termX / 2) - 8, termY - 1, 6, 1, green, gray, "Save")
  1638.         end
  1639.         drawElement(11, 6, 1, 1, yellow, black, addressBook[currentEdit].name .. "  ")  --# clean up after the rating pop-up
  1640.         drawElement(11, 8, 1, 1, assignColor(currentEdit), nil, addressBook[currentEdit].addr .. "  ")
  1641.         drawElement(18, 12, 1, 1, nil, nil, assignRating(currentEdit))
  1642.         drawElement(2, 14, 1, 1, gray, nil, "Dimension:")
  1643.         drawElement(12, 15, 2, 1, nil, nil, "x:")
  1644.         drawElement(25, 15, 2, 1, nil, nil, "y:")
  1645.         if addressBook[currentEdit].addr == thisGate or addressBook[currentEdit].addr == thisGate:sub(1, 7) then
  1646.           drawElement(termX / 2, 8, 1, 1, nil, nil, "< THIS GATE >")
  1647.         end
  1648.         drawElement(8, 10, 1, 1, white, nil, addressBook[currentEdit].note)
  1649.         drawElement(13, 14, 1, 1, yellow, nil, addressBook[currentEdit].loc.dim)
  1650.         drawElement(15, 15, 1, 1, silver, nil, addressBook[currentEdit].loc.x)
  1651.         drawElement(28, 15, 1, 1, nil, nil, addressBook[currentEdit].loc.y)
  1652.         drawElement(25, 6, 1, 1, gray, nil, "Iris:")
  1653.         if addressBook[currentEdit].iris == "none" then                               --# Iris control
  1654.           drawElement(31, 6, "mid")
  1655.         elseif addressBook[currentEdit].iris == "open" then
  1656.           drawElement(31, 6, true)
  1657.         elseif addressBook[currentEdit].iris == "close" then
  1658.           drawElement(31, 6, false)
  1659.         end
  1660.       end
  1661.     elseif mcY == 14 and mButton == 1 then --# Gate Dimension
  1662.       if mcX > 12 and mcX < 13 + #addressBook[currentEdit].loc.dim then
  1663.         drawElement(13, 14, 1, 1, gray, black, addressBook[currentEdit].loc.dim)
  1664.         local newDim = readInput(13, 14, yellow, black, 19)
  1665.         if newDim ~= "" and newDim ~= "nil" then
  1666.           addressBook[currentEdit].loc.dim = newDim
  1667.           drawElement(13, 14, 38, 1, yellow, black, "")
  1668.           gateChange = true
  1669.         end
  1670.         drawElement(13, 14, 1, 1, yellow, black, addressBook[currentEdit].loc.dim .. string.rep(" ", 19 - #addressBook[currentEdit].loc.dim))
  1671.       end
  1672.     elseif mcY == 15 and mButton == 1 then --# Gate X,Y,Z Coordinates
  1673.       if mcX > 14 and mcX < 15 + #tostring(addressBook[currentEdit].loc.x) then --# X coordinate
  1674.         local oldX = tostring(addressBook[currentEdit].loc.x)
  1675.         drawElement(15, 15, 1, 1, gray, black, oldX)
  1676.         local newX = tonumber(readInput(15, 15, silver, black, 9))
  1677.         if tostring(newX) ~= "" and newX ~= nil and newX ~= addressBook[currentEdit].loc.x then
  1678.           addressBook[currentEdit].loc.x = newX
  1679.           gateChange = true
  1680.         end
  1681.         drawElement(15, 15, 1, 1, silver, black, tostring(addressBook[currentEdit].loc.x) .. string.rep(" ", 9 - #tostring(addressBook[currentEdit].loc.x)))
  1682.       elseif mcX > 27 and mcX < 28 + #tostring(addressBook[currentEdit].loc.y) then --# Y coordinate
  1683.         local oldY = tostring(addressBook[currentEdit].loc.y)
  1684.         drawElement(28, 15, 1, 1, gray, black, oldY)
  1685.         local newY = tonumber(readInput(28, 15, silver, black, 9))
  1686.         if tostring(newY) ~= "" and newY ~= nil and newY ~= addressBook[currentEdit].loc.y then
  1687.           addressBook[currentEdit].loc.y = newY
  1688.           gateChange = true
  1689.         end
  1690.         drawElement(28, 15, 1, 1, silver, black, tostring(addressBook[currentEdit].loc.y) .. string.rep(" ", 9 - #tostring(addressBook[currentEdit].loc.y)))
  1691.       elseif mcX > 40 and mcX < 41 + #tostring(addressBook[currentEdit].loc.z) then --# Z coordinate
  1692.         local oldZ = tostring(addressBook[currentEdit].loc.z)
  1693.         drawElement(41, 15, 1, 1, gray, black, oldZ)
  1694.         local newZ = tonumber(readInput(41, 15, silver, black, 9))
  1695.         if tostring(newZ) ~= "" and newZ ~= nil and newZ ~= addressBook[currentEdit].loc.z then
  1696.           addressBook[currentEdit].loc.z = newZ
  1697.           gateChange = true
  1698.         end
  1699.         drawElement(41, 15, 1, 1, silver, black, tostring(addressBook[currentEdit].loc.z) .. string.rep(" ", 9 - #tostring(addressBook[currentEdit].loc.z)))
  1700.       end
  1701.     elseif mcY == (termY - 1) and mButton == 1 then         --# Save Address Book
  1702.       if mcX > (termX / 2) - 9 and mcX < (termX / 2) - 3 and gateChange then
  1703.         flashChoice((termX / 2) - 8, termY - 1, white, green, " Save ")
  1704.         drawElement((termX / 2) - 8, termY - 1, 1, 1, silver, gray, " Save ")
  1705.         saveData(gateData, "gate")
  1706.         --return
  1707.       elseif mcX > termX / 2 and mcX < (termX / 2) + 7 then --# Exit View/Edit Screen
  1708.         runState = "Dial"
  1709.         flashChoice((termX / 2) + 1, termY - 1, white, red, " Close ")
  1710.         drawElement(2, 4, termX, termY, nil, black, "")
  1711.         drawCLI(true)
  1712.         return
  1713.       end
  1714.     end
  1715.     if gateChange then drawElement((termX / 2) - 8, termY - 1, 6, 1, green, gray, " Save ") end
  1716.   end
  1717. end
  1718.  
  1719. local function settingsChange()
  1720.   configChange = true
  1721.   drawElement(45, 4, 6, 1, lime, silver, " Save  ")
  1722. end
  1723.  
  1724. local function editSettings()
  1725.   while true do
  1726.     local _, mButton, mcX, mcY = os.pullEvent("mouse_click")
  1727.     local cfgPos = { 5, 7, 9, 11, 13, 15 }
  1728.     local cfgSides = { "top", "bottom", "front", "back", "left", "right" }
  1729.       --# Close Settings (no save)
  1730.     if mcY == 2 and mcX > 44 and mButton == 1 then
  1731.       flashChoice(termX - 6, 2, white, red, " Close ")
  1732.       runState = "Dial"
  1733.       clearLowerScreen()
  1734.       drawCLI()
  1735.       return
  1736.       --# Save Settings
  1737.     elseif mcY == 4 and mcX > 44 and mButton == 1 and configChange then
  1738.       flashChoice(45, 4, white, green, " Save  ")
  1739.       drawElement(45, 4, 1, 1, gray, silver, " Save  ")
  1740.       saveData(settingsData, "cfg")
  1741.       configChange = false
  1742.     end
  1743.       --# Settings Column 1
  1744.       --# Change LOCKDOWN Password
  1745.     if mcY == 6 and mcX > 16 and mcX < 26 and mButton == 1 then
  1746.       runState = "newPassword"
  1747.       return
  1748.       --# Biolock ON/OFF
  1749.     elseif mcY == 10 and mcX > 21 and mcX < 26 and hardware.bio > 0 and mButton == 1 then
  1750.       dhdSettings.bio.lock = not dhdSettings.bio.lock
  1751.       drawElement(22, 10, dhdSettings.bio.lock)
  1752.       settingsChange()
  1753.       --# Bioscanner function (iris/lockdown)
  1754.     elseif mcY == 12 and mcX > 21 and mcX < 26 and hardware.bio > 0 and mButton == 1 then
  1755.       if dhdSettings.bio.func == "none" then
  1756.         dhdSettings.bio.func = "iris"
  1757.         drawElement(7, 12, 1, 1, green, black, "Iris       ")
  1758.         drawElement(22, 12, true)
  1759.       elseif dhdSettings.bio.func == "iris" then
  1760.         dhdSettings.bio.func = "lock"
  1761.         drawElement(7, 12, 1, 1, red, black, "Lockdown   ")
  1762.         drawElement(22, 12, false)
  1763.       elseif dhdSettings.bio.func == "lock" then
  1764.         dhdSettings.bio.func = "none"
  1765.         drawElement(7, 12, 1, 1, silver, black, "No Function")
  1766.         drawElement(22, 12, "MID")
  1767.       end
  1768.       settingsChange()
  1769.       --# Set Biolock login authorization level
  1770.     elseif mcY == 14 and mcX > 19 and mcX < 25 and hardware.bio > 0 and mButton == 1 then
  1771.       if dhdSettings.bio.auth ~= mcX - 19 then
  1772.         dhdSettings.bio.auth = mcX - 19
  1773.         drawElement(19, 14, 1, 1, gray, black, "|-----|")
  1774.         drawElement(19 + dhdSettings.bio.auth, 14, 1, 1, black, silver, tostring(dhdSettings.bio.auth))
  1775.         settingsChange()
  1776.       end
  1777.       --# Set Biolock iris/lockdown authorization level
  1778.     elseif mcY == 16 and mcX > 19 and mcX < 25 and hardware.bio > 0 and mButton == 1 then
  1779.       if dhdSettings.bio.fAuth ~= mcX - 19 then
  1780.         dhdSettings.bio.fAuth = mcX - 19
  1781.         drawElement(19, 16, 1, 1, gray, black, "|-----|")
  1782.         drawElement(19 + dhdSettings.bio.fAuth, 16, 1, 1, black, silver, tostring(dhdSettings.bio.fAuth))
  1783.         settingsChange()
  1784.       end
  1785.       --# Settings Column 2
  1786.       --# pSync ON/OFF for ccDialer
  1787.     elseif mcY == 6 and mcX > 45 and mcX < 50 and mButton == 1 then
  1788.       dhdSettings.pSync = not dhdSettings.pSync
  1789.       if dhdSettings.pSync then
  1790.         if not rednet.isOpen(hardware.modemSide) then
  1791.           rednet.open(hardware.modemSide)
  1792.         end
  1793.         drawElement(46, 6, 1, 1, nil, orange, "  ") --# 'pSync initializing' switch position (orange)
  1794.         term.setBackgroundColor(gray)
  1795.         term.write("  ")
  1796.         drawElement(termX / 2 - 7, math.floor(termY / 2) - 2, 20, 1, white, blue, "pSync ")
  1797.         drawElement(termX / 2 - 7, math.floor(termY / 2) - 1, 20, 3, nil, silver, "Initializing...")
  1798.         rednet.host("pSync", thisGate)
  1799.         drawElement(termX / 2 - 7, math.floor(termY / 2) - 2, 20, 4, nil, black, "")
  1800.         drawSettingsUI()
  1801.       else
  1802.         rednet.unhost("pSync")
  1803.         drawElement(46, 6, false)
  1804.       end
  1805.       settingsChange()
  1806.       --# Highlight thisGate in Dial/Trim modes & on List monitor
  1807.     elseif mcY == 10 and mcX > 45 and mcX < 50 and mButton == 1 then
  1808.       dhdSettings.highlight = not dhdSettings.highlight
  1809.       drawElement(46, 10, dhdSettings.highlight)
  1810.       settingsChange()
  1811.       --# Call Logging (ON/OFF)
  1812.     elseif mcY == 12 and mcX > 45 and mcX < 50 and mButton == 1 then
  1813.       dhdSettings.logs = not dhdSettings.logs
  1814.       drawElement(46, 12, dhdSettings.logs)
  1815.       settingsChange()
  1816.       --# Startup Iris (CLOSED/OPEN)
  1817.     elseif mcY == 14 and mcX > 45 and mcX < 50 and mButton == 1 then
  1818.       dhdSettings.startIris = not dhdSettings.startIris
  1819.       drawElement(46, 14, dhdSettings.startIris)
  1820.       settingsChange()
  1821.       --# End Call Iris (CLOSED/OPEN)
  1822.     elseif mcY == 16 and mcX > 45 and mcX < 50 and mButton == 1 then
  1823.       dhdSettings.ecIris = not dhdSettings.ecIris
  1824.       drawElement(46, 16, dhdSettings.ecIris)
  1825.       settingsChange()
  1826.     end
  1827.   end
  1828. end
  1829.  
  1830. local function keyClick()
  1831.   while true do
  1832.     local _, evKey = os.pullEvent("key")
  1833.     if runState == "Dial" and evKey == 59 then --# HELP (F1)
  1834.       runState = "DHDhelp"
  1835.       drawHelpUI()
  1836.       return
  1837.     elseif runState == "DHDhelp" and evKey == 59 then
  1838.       flashChoice(termX - 6, 2, white, red, " Close ")
  1839.       runState = "Dial"
  1840.       clearLowerScreen()
  1841.       drawCLI()
  1842.       return
  1843.     elseif runState == "Dial" or runState == "Trim" then
  1844.       if evKey == 199 then     --# HOME
  1845.         gatePage = 1
  1846.         drawAddressBook()
  1847.         drawNaviUI()
  1848.       elseif evKey == 207 then --# END
  1849.         gatePage = gatePages
  1850.         if gatePages > 1 then
  1851.           drawElement(2, 6, 39, 11, nil, black, "")
  1852.         end
  1853.         drawAddressBook()
  1854.         drawNaviUI()
  1855.       elseif evKey == 201 then --# pageUP
  1856.         gatePage = math.max(1, gatePage - 1)
  1857.         drawAddressBook()
  1858.         drawNaviUI()
  1859.       elseif evKey == 209 then --# pageDN
  1860.         gatePage = math.min(gatePage + 1, gatePages)
  1861.         if gatePage == gatePages and gatePages > 1 then
  1862.           drawElement(2, 6, 39, 11, nil, black, "")
  1863.         end
  1864.         drawAddressBook()
  1865.         drawNaviUI()
  1866.       end
  1867.     elseif runState == "logs" then
  1868.       if evKey == 199 then     --# HOME
  1869.         logPage = 1
  1870.         drawLogScreen()
  1871.       elseif evKey == 207 then --# END
  1872.         logPage = logPages
  1873.         drawLogScreen()
  1874.       elseif evKey == 201 then --# pageUP
  1875.         logPage = math.max(1, logPage - 1)
  1876.         drawLogScreen()
  1877.       elseif evKey == 209 then --# pageDN
  1878.         logPage = math.min(logPage + 1, logPages)
  1879.         drawLogScreen()
  1880.       end
  1881.     end
  1882.   end
  1883. end
  1884.  
  1885. local function mClick()
  1886.   while true do
  1887.     local _, mButton, mcX, mcY = os.pullEvent("mouse_click")
  1888.     --# Menu & Menu Selections
  1889.     if menuState then
  1890.       if mcX > 1 and mcX < 12 then
  1891.         if mcY == 3 and mButton == 1 then
  1892.           flashChoice(3, 3, white, sky, "Settings ")
  1893.           runState = "DHDsettings"
  1894.           menuState = false
  1895.           clearLowerScreen()
  1896.           drawSettingsUI()
  1897.           return
  1898.         elseif mcY == 5 and mButton == 1 then
  1899.           flashChoice(3, 5, black, yellow, "View Logs")
  1900.           runState = "logs"
  1901.           menuState = false
  1902.           ingestData("logs")
  1903.           clearLowerScreen()
  1904.           drawHeader()
  1905.           drawLogScreen()
  1906.           return
  1907.         elseif mcY == 7 and mButton == 1 then
  1908.           flashChoice(3, 7, white, cyan, "DHD Help ")
  1909.           runState = "DHDhelp"
  1910.           menuState = false
  1911.           drawHelpUI()
  1912.           return
  1913.         elseif mcY == 9 and mButton == 1 then
  1914.           flashChoice(3, 9, white, red, "  EXIT   ")
  1915.           if gateChange or configChange then
  1916.             runState = "exodus"
  1917.             menuState = false
  1918.             drawElement(2, 5, 11, 6, nil, black, "")       --# clear menu
  1919.             drawHeader()
  1920.             drawAddressBook()
  1921.             drawPopUp()
  1922.             return
  1923.           else
  1924.             kernelState = false
  1925.             menuState = false
  1926.             break --# required to exit properly
  1927.           end
  1928.         elseif mcY < 2 or mcY > 10 then
  1929.           menuState = false
  1930.           drawElement(2, 5, 11, 6, nil, black, "")         --# clear menu
  1931.           drawHeader()
  1932.           drawAddressBook()
  1933.           --return
  1934.         end
  1935.       else
  1936.         menuState = false
  1937.         drawElement(2, 5, 11, 6, nil, black, "")           --# clear menu
  1938.         drawHeader()
  1939.         drawAddressBook()
  1940.         --return
  1941.       end
  1942.     else
  1943.       if runState == "DHDhelp" then                        --# DHD Help Screen
  1944.         if mcX > 44 and mcY == 2 and mButton == 1 then
  1945.           flashChoice(termX - 6, 2, white, red, " Close ")
  1946.           runState = "Dial"
  1947.           clearLowerScreen()
  1948.           drawCLI()
  1949.           return
  1950.         end
  1951.       elseif runState == "logs" then                       --# Log Screen
  1952.         if mcX > 44 and mcY == 2 and mButton == 1 then
  1953.           flashChoice(termX - 6, 2, white, red, " Close ")
  1954.           runState = "Dial"
  1955.           clearCallHistory()
  1956.           lastCall = nil
  1957.           clearLowerScreen()
  1958.           drawCLI()
  1959.           return
  1960.         elseif mcX > 44 and mcY == 4 and mButton == 1 then
  1961.           flashChoice(termX - 6, 4, white, orange, " Clear ")
  1962.           drawElement(termX - 6, 4, 1, 1, orange, silver, " Clear ")
  1963.           runState = "clearLogs"
  1964.           return
  1965.         elseif mcY == termY and mButton == 1 then
  1966.           if mcX > 11 and mcX < 14 then                    --# Home
  1967.             logPage = 1
  1968.             drawLogScreen()
  1969.           elseif mcX > 14 and mcX < 16 then                --# PageBack
  1970.             logPage = math.max(1, logPage - 1)
  1971.             drawLogScreen()
  1972.           elseif mcX > 18 and mcX < 33 then                --# Page Entry
  1973.             flashChoice(math.floor((termX / 2) - 3 - math.floor((#tostring(logPage) + #tostring(logPages)) / 2)), termY, gray, silver, " " .. tostring(logPage) .. " of " .. tostring(logPages) .. " ")
  1974.             drawElement(math.floor((termX / 2) - 3 - math.floor((#tostring(logPage) + #tostring(logPages)) / 2)), termY, 1, 1, silver, gray, " " .. tostring(logPage) .. " of " .. tostring(logPages) .. " ")
  1975.             runState = "goLogPage"
  1976.             drawPopUp()
  1977.             return
  1978.           elseif mcX > 35 and mcX < 37 then                --# PageForward
  1979.             logPage = math.min(logPage + 1, logPages)
  1980.             drawLogScreen()
  1981.           elseif mcX > 37 and mcX < 40 then                --# End
  1982.             logPage = logPages
  1983.             drawLogScreen()
  1984.           end
  1985.         elseif mcY == 3 and mcX > 30 and mcX < 40 then     --# last call
  1986.           local callAddress = lastCall:sub(string.find(lastCall, ">") + 2, #lastCall)
  1987.           if mButton == 1 and (#callAddress == 7 or #callAddress == 9) then
  1988.             runState = "Dial"
  1989.             clearCallHistory()
  1990.             lastCall = nil
  1991.             netSend(callAddress)
  1992.             clearLowerScreen()
  1993.             drawCLI()
  1994.             return
  1995.           elseif mButton == 2 then
  1996.             runState = "GateEdit"
  1997.             addNewAddress(callAddress)
  1998.             return
  1999.           end
  2000.         elseif mcY > 5 and mcY < termY - 2 and mcX > 30 and mcX < 40 then --# call log
  2001.           local currentEntry = ((logPage - 1) * 11) + 1    --# Set the first entry (based on page number)
  2002.           if callHistory[currentEntry + mcY - 6] then
  2003.             local callAddress = callHistory[currentEntry + mcY - 6]:sub(string.find(callHistory[currentEntry + mcY - 6], ">") + 2, #callHistory[currentEntry + mcY - 6])
  2004.             if mButton == 1 and (#callAddress == 7 or #callAddress == 9) then
  2005.               runState = "Dial"
  2006.               clearCallHistory()
  2007.               lastCall = nil
  2008.               netSend(callAddress)
  2009.               clearLowerScreen()
  2010.               drawCLI()
  2011.               return
  2012.             elseif mButton == 2 and (#callAddress == 7 or #callAddress == 9) then
  2013.               runState = "GateEdit"
  2014.               addNewAddress(callAddress)
  2015.               return
  2016.             end
  2017.           end
  2018.         end
  2019.       elseif runState == "Dial" then                       --# Open Menu
  2020.         if mcY == 1 and mcX > 1 and mcX < 7 and mButton == 1 then
  2021.           drawMenu()
  2022.           --return
  2023.         end
  2024.       end
  2025.       --# Command Buttons (change modes, mode operation)
  2026.       if mcX > 42 and (runState == "Dial" or runState == "Trim") and secureStatus == "allclear" then
  2027.         if mcY == 7 and mButton == 1 and runState == "Dial" then
  2028.           if not irisState then
  2029.             drawWaitingIrisState()
  2030.             netSend("iCLOSE")
  2031.             waitingForIris = true
  2032.           elseif irisState and secureStatus == "allclear" then
  2033.             drawWaitingIrisState(true)
  2034.             netSend("iOPEN")
  2035.             waitingForIris = true
  2036.           end
  2037.         elseif mcY == 9 and mButton == 1 and runState == "Dial" then
  2038.           if gateStatus == "Connected" or gateStatus == "Dialing" then
  2039.             flashChoice(43, 9, orange, black, "END Call ")
  2040.             drawElement(43, 9, 1, 1, black, orange, "END Call ")
  2041.             netSend("endCall")
  2042.           end
  2043.         elseif mcY == 11 and mButton == 1 then
  2044.           if runState == "Dial" then
  2045.             flashChoice(43, 11, red, orange, "LOCKDOWN ")
  2046.             drawElement(43, 11, 1, 1, orange, red, "LOCKDOWN ")
  2047.             netSend("lockdown")
  2048.             secureStatus = "lockdown"
  2049.             oldOsPullEvent = os.pullEvent
  2050.             os.pullEvent = os.pullEventRaw
  2051.             return
  2052.           elseif runState == "Trim" and gateChange then
  2053.             flashChoice(43, 11, blue, white, "Reload   ")  --# Undo/Reload
  2054.             drawElement(43, 11, 1, 1, gray, sky, "Reload   ")
  2055.             drawElement(43, 13, 1, 1, nil, green, "Save     ")
  2056.             ingestData(gateData, "gate")
  2057.             gatePage = math.min(gatePage, gatePages)
  2058.             drawElement(2, 6, 39, 11, nil, black, "")
  2059.             gateChange = false
  2060.             drawAddressBook()
  2061.             drawNaviUI()
  2062.             if displayState == "info" then displayState = "list" end
  2063.             displayAddressBook()
  2064.             displayMarquee()
  2065.           end
  2066.         elseif mcY == 13 then
  2067.           if runState == "Dial" then
  2068.             if mButton == 1 then
  2069.               flashChoice(43, 13, blue, white, "AddrBook ")
  2070.               drawElement(43, 13, 1, 1, gateChange and lime or white, blue, "AddrBook ")
  2071.               runState = "importExport"
  2072.               drawPopUp()
  2073.               return
  2074.             elseif mButton == 2 and gateChange then
  2075.               flashChoice(43, 13, blue, white, "AddrBook ")
  2076.               drawElement(43, 13, 1, 1, white, blue, "AddrBook ")
  2077.               saveData(gateData, "gate")
  2078.             end
  2079.           elseif runState == "Trim" and mButton == 1 and gateChange then
  2080.             flashChoice(43, 13, green, white, "Save     ") --# Commit/Save
  2081.             drawElement(43, 11, 1, 1, gray, sky, "Reload   ")
  2082.             drawElement(43, 13, 1, 1, nil, green, "Save     ")
  2083.             saveData(gateData, "gate")
  2084.           end
  2085.         elseif mcY == 15 then
  2086.           if runState == "Dial" then
  2087.             if mButton == 1 then
  2088.               flashChoice(43, 15, green, white, "New Gate ")
  2089.               runState = "GateEdit"
  2090.               addNewAddress()
  2091.               return
  2092.             elseif mButton == 3 then
  2093.               flashChoice(43, 15, green, white, "New Gate ")
  2094.               drawElement(43, 15, 1, 1, white, green, "New Gate ")
  2095.               drawElement(43, 13, 1, 1, lime, blue, "AddrBook ")
  2096.               addNewAddress(nil, true)
  2097.             end
  2098.           elseif runState == "Trim" and mButton == 1 then
  2099.             flashChoice(43, 15, red, white, "Close    ")   --# Close/Exit Trim
  2100.             runState = "Dial"
  2101.             drawCLI()
  2102.             return
  2103.           end
  2104.         end
  2105.       end
  2106.     end
  2107.      --# Page Navigation via click
  2108.     if mcY == termY and mButton == 1 and secureStatus == "allclear" then
  2109.       if runState == "Dial" or runState == "Trim" then
  2110.         if mcX > 9 and mcX < 12 and mcY == termY then      --# Home
  2111.           gatePage = 1
  2112.           drawAddressBook()
  2113.           drawNaviUI()
  2114.         elseif mcX > 12 and mcX < 14 and mcY == termY then --# Back
  2115.           if gatePage > 1 then
  2116.             gatePage = math.max(1, gatePage - 1)
  2117.             drawAddressBook()
  2118.             drawNaviUI()
  2119.           end
  2120.         elseif mcX > 16 and mcX < 25 and mcY == termY then --# Page Numbers (Go To Page dialogue)
  2121.           flashChoice(((termX / 2) - 3) - (#pNum / 2) - 2, termY, black, gray, " " .. pNum .. " ")
  2122.           drawElement(((termX / 2) - 3) - (#pNum / 2) - 2, termY, #pNum, 1, gray, black, " " .. pNum .. " ")
  2123.           runState = runState == "Dial" and "goPage" or "goTrimPage"
  2124.           drawPopUp()
  2125.           return
  2126.         elseif mcX > 27 and mcX < 29 and mcY == termY then --# Forward
  2127.           if gatePage < gatePages then
  2128.             gatePage = math.min(gatePage + 1, gatePages)
  2129.             if gatePage == gatePages and gatePages > 1 then drawElement(2, 6, 39, 11, nil, black, "") end
  2130.             drawAddressBook()
  2131.             drawNaviUI()
  2132.           end
  2133.         elseif mcX > 29 and mcX < 32 and mcY == termY then --# End
  2134.           gatePage = gatePages
  2135.           if gatePages > 1 then drawElement(2, 6, 39, 11, nil, black, "") end
  2136.           drawAddressBook()
  2137.           drawNaviUI()
  2138.         end
  2139.       end
  2140.     end
  2141.      --# Dial a listed address, view it's info, or enter trim mode & delete gates
  2142.     if not menuState and (runState == "Dial" or runState == "Trim") and secureStatus == "allclear" then
  2143.       if mcX > 1 and mcX < 41 and mcY > 5 and mcY < 17 then
  2144.         local magicNumber = ((gatePage - 1) * 23) + gatePage
  2145.         local xPos, yPos = 2, 6
  2146.         for i = magicNumber, #addressBook do
  2147.           if mcX >= xPos and mcX <= xPos + 8 and mcY == yPos then
  2148.             if runState == "Dial" then
  2149.               if mButton == 1 and gateStatus == "Disconnected" then
  2150.                 if (#addressBook[i].addr == 9 and addressBook[i].addr == thisGate) or (#addressBook[i].addr == 7 and addressBook[i].addr == thisGate:sub(1, 7)) then
  2151.                   --# do nothing
  2152.                 else
  2153.                   flashDial(addressBook[i].name, xPos, mcY, i)
  2154.                   dialAddress = addressBook[i].addr
  2155.                   netSend(dialAddress)
  2156.                 end
  2157.               elseif mButton == 2 then
  2158.                 flashDial(addressBook[i].name, xPos, mcY, i)
  2159.                 runState = "GateEdit"
  2160.                 currentEdit = i
  2161.                 drawGateLabels()
  2162.                 return
  2163.               elseif mButton == 3 then
  2164.                 runState = "Trim"
  2165.                 drawCLI()
  2166.                 return
  2167.               end
  2168.             elseif runState == "Trim" and mButton == 1 then
  2169.               deleteGate(i)
  2170.               if displayState == "info" then displayState = "list" end
  2171.               displayAddressBook()
  2172.               displayMarquee()
  2173.               drawAddressBook()
  2174.               drawNaviUI()
  2175.             end
  2176.           end
  2177.           yPos = yPos + 2
  2178.           if yPos > 16 then yPos = 6 xPos = xPos + 10 end
  2179.           if xPos > 32 then break end
  2180.         end
  2181.       end
  2182.     end
  2183.   end
  2184. end
  2185.  
  2186. local function mScroll()
  2187.   while true do
  2188.     local _, data = os.pullEvent("mouse_scroll")
  2189.     if runState == "Dial" or runState == "Trim" then
  2190.       if data == -1 and gatePage > 1 then
  2191.         gatePage = gatePage - 1
  2192.         if menuState then
  2193.           drawElement(2, 2, 10, 11, nil, black, "")
  2194.           menuState = false
  2195.           drawHeader()
  2196.         end
  2197.         drawAddressBook()
  2198.         drawNaviUI()
  2199.       elseif data == 1 and gatePage < gatePages then
  2200.         gatePage = gatePage + 1
  2201.         local doHeader = false
  2202.         if menuState then
  2203.           drawElement(2, 2, 10, 11, nil, black, "")
  2204.           menuState = false
  2205.           doHeader = true
  2206.         end
  2207.         if gatePage == gatePages and gatePages > 1 then
  2208.           drawElement(2, 6, 39, 11, nil, black, "")
  2209.           doHeader = true
  2210.         end
  2211.         if doHeader then drawHeader() end
  2212.         drawAddressBook()
  2213.         drawNaviUI()
  2214.       end
  2215.     elseif runState == "GateEdit" then
  2216.       currentEdit = data == 1 and math.min(currentEdit + 1, #addressBook) or math.max(1, currentEdit - 1)
  2217.       if gettingInput then
  2218.         term.setCursorBlink(false)
  2219.         gettingInput = false
  2220.       end
  2221.       drawGateData()
  2222.     elseif runState == "logs" then
  2223.       logPage = data == 1 and math.min(logPage + 1, logPages) or math.max(1, logPage - 1)
  2224.       drawLogScreen()
  2225.     end
  2226.   end
  2227. end
  2228.  
  2229. local function monTouch()
  2230.   while true do
  2231.     local _, monitor, touchX, touchY = os.pullEvent("monitor_touch")
  2232.     if secureStatus == "allclear" then
  2233.       local match = false
  2234.       for i = 1, #listMonSides do
  2235.         if monitor == listMonSides[i] then match = true break end
  2236.       end
  2237.       if match and displayState == "info" then
  2238.         displayState = "list"
  2239.         drawCLI(true)
  2240.       elseif match and displayState == "list" then
  2241.         if touchY < 9 then
  2242.           if (listPage - 1) * 8 + touchY <= #addressBook then
  2243.             selectedGate = (listPage - 1) * 8 + touchY
  2244.             displayState = "info"
  2245.             if hardware.marquee > 0 then displayNotes(selectedGate) end
  2246.           end
  2247.         elseif touchY > 8 then
  2248.           if touchX < 10 then
  2249.             listPage = math.max(1, listPage - 1)
  2250.             displayAddressBook()
  2251.           elseif touchX > 9 then
  2252.             listPage = math.min(listPage + 1, listPages)
  2253.             displayAddressBook()
  2254.           end
  2255.         end
  2256.       end
  2257.     end
  2258.     local match = false
  2259.     for i = 1, #marqueeSides do
  2260.       if monitor == marqueeSides[i] then match = true break end
  2261.     end
  2262.     if match and displayState == "info" and gateStatus == "Disconnected" and secureStatus == "allclear" then
  2263.       if #addressBook[selectedGate].addr == 9 and addressBook[selectedGate].addr ~= thisGate then
  2264.         dialAddress = addressBook[selectedGate].addr
  2265.         netSend(dialAddress)
  2266.       elseif #addressBook[selectedGate].addr == 7 and addressBook[selectedGate].addr ~= thisGate:sub(1, 7) then
  2267.         dialAddress = addressBook[selectedGate].addr
  2268.         netSend(dialAddress)
  2269.       end
  2270.     elseif match and gateStatus ~= "Disconnected" then
  2271.       netSend("endCall")
  2272.     end
  2273.   end
  2274. end
  2275.  
  2276. local function bioScan() -- "biolock", IDprint, attachSide, learnedName, accessLevel
  2277.   while true do
  2278.     local fistPrint = { os.pullEvent("biolock") }
  2279.     if fistPrint[1] == "biolock" then --# this is here because ^T otherwise causes an error 8 lines down during lockdown
  2280.       if runState == "init" then
  2281.         if fistPrint[5] >= dhdSettings.bio.auth then
  2282.           fistAuth = true
  2283.           return fistAuth
  2284.         end
  2285.         return false
  2286.       else
  2287.         if fistPrint[5] >= dhdSettings.bio.fAuth then
  2288.           if dhdSettings.bio.func == "iris" then
  2289.             if not irisState then
  2290.               if runState == "Dial" or runState == "goPage" or runState == "importExport" or runState == "exodus" then
  2291.                 drawWaitingIrisState()
  2292.                 if gettingInput then redrawInput() end
  2293.               end
  2294.               netSend("iCLOSE")
  2295.               waitingForIris = true
  2296.             elseif irisState and secureStatus == "allclear" then
  2297.               if runState == "Dial" or runState == "goPage" or runState == "importExport" or runState == "exodus" then
  2298.                 drawWaitingIrisState(true)
  2299.                 if gettingInput then redrawInput() end
  2300.               end
  2301.               netSend("iOPEN")
  2302.               waitingForIris = true
  2303.             end
  2304.           elseif dhdSettings.bio.func == "lock" then
  2305.             if secureStatus == "allclear" then
  2306.               local states = {
  2307.                 Dial = "Dial";
  2308.                 Trim = "Trim";
  2309.                 GateEdit = "GateEdit";
  2310.                 DHDhelp = "DHDhelp";
  2311.                 DHDsettings = "DHDsettings";
  2312.                 logs = "logs";
  2313.                 goPage = "Dial",
  2314.                 importExport = "Dial",
  2315.                 exodus = "Dial",
  2316.                 goTrimPage = "Trim",
  2317.                 newPassword = "DHDsettings",
  2318.                 clearLogs = "logs",
  2319.                 goLogPage = "logs",
  2320.               }
  2321.               runState = states[runState]
  2322.               menuState = false
  2323.               if gettingInput then
  2324.                 gettingInput = false
  2325.                 term.setCursorBlink(false)
  2326.               end
  2327.               netSend("lockdown")
  2328.               secureStatus = "lockdown"
  2329.               oldOsPullEvent = os.pullEvent
  2330.               os.pullEvent = os.pullEventRaw
  2331.               clearLowerScreen()
  2332.               drawSecureUI()
  2333.               return     --# doing this will cycle dhdKernel
  2334.             else
  2335.               netSend("allclear")
  2336.               secureStatus = "allclear"
  2337.               drawElement(15, 9, termX - 14, 1, nil, black, "") --# clear !!LOCKDOWN!!
  2338.               drawElement(15, 15, termX - 14, 1, nil, nil, "")  --# clear password input
  2339.               os.pullEvent = oldOsPullEvent
  2340.               oldOsPullEvent = nil
  2341.               term.setCursorBlink(false)
  2342.               gettingInput = false
  2343.               waitingForIris = true
  2344.               if displayState == "list" then
  2345.                 displayStatus()
  2346.                 displayAddressBook()
  2347.               else
  2348.                 displayNotes(selectedGate)
  2349.               end
  2350.               local states = {
  2351.                 Dial = function() end;
  2352.                 Trim = function() end;
  2353.                 GateEdit = function() drawGateLabels() end,
  2354.                 logs = function() drawHeader() drawLogScreen() end,
  2355.                 DHDsettings = function() drawSettingsUI() end,
  2356.                 DHDhelp = function() drawHelpUI() end,
  2357.               }
  2358.               states[runState]()
  2359.               return     --# doing this will cycle dhdKernel
  2360.             end
  2361.           end
  2362.         end
  2363.       end
  2364.     end
  2365.   end
  2366. end
  2367.  
  2368. local function bioAnimation() --# not using drawElement intentionally for performance
  2369.   while true do
  2370.     for i = 2, 20 do
  2371.       term.setCursorPos(i, 4)
  2372.       term.setBackgroundColor(blue)
  2373.       term.write(" ")
  2374.       term.setBackgroundColor(black)
  2375.       term.setCursorPos(i - 1, 4)
  2376.       term.write(" ")
  2377.       sleep(0.04)
  2378.     end
  2379.     for i = 20, 2, -1 do
  2380.       term.setCursorPos(i, 4)
  2381.       term.setBackgroundColor(blue)
  2382.       term.write(" ")
  2383.       term.setBackgroundColor(black)
  2384.       term.setCursorPos(i + 1, 4)
  2385.       term.write(" ")
  2386.       sleep(0.04)
  2387.     end
  2388.   end
  2389. end
  2390.  
  2391. local function bioLogin()
  2392.   if hardware.bio == 0 then return end
  2393.   oldOsPullEvent = os.pullEvent
  2394.   os.pullEvent = os.pullEventRaw
  2395.   fistAuth = false
  2396.   clearScreen()
  2397.   drawElement(2, 2, 19, 1, yellow, nil, "Waiting for Bioscan")
  2398.   if hardware.listMon > 0 then
  2399.     for i = 1, hardware.listMon do
  2400.       listMon[i].setBackgroundColor(black)
  2401.       listMon[i].clear()
  2402.       listMon[i].setTextColor(yellow)
  2403.       listMon[i].setTextScale(1)
  2404.       listMon[i].setCursorPos(1, 2)
  2405.       listMon[i].write("Waiting")
  2406.       listMon[i].setCursorPos(3, 3)
  2407.       listMon[i].write("for")
  2408.       listMon[i].setCursorPos(1, 4)
  2409.       listMon[i].write("Bioscan")
  2410.     end
  2411.   end
  2412.   while true do
  2413.     parallel.waitForAny(bioScan, bioAnimation)
  2414.     if fistAuth then
  2415.       if oldOsPullEvent then
  2416.         os.pullEvent = oldOsPullEvent
  2417.         oldOsPullEvent = nil
  2418.       end
  2419.       return
  2420.     end
  2421.   end
  2422. end
  2423.  
  2424. do
  2425.   local states = {
  2426.     Dial = function() end;
  2427.     Trim = function() end;
  2428.     GateEdit = function() drawGateLabels() end,
  2429.     logs = function() drawHeader() drawLogScreen() end,
  2430.     DHDsettings = function() drawSettingsUI() end,
  2431.     DHDhelp = function() drawHelpUI() end,
  2432.   }
  2433.  
  2434.   secKernel = function() --# LOCKDOWN handler
  2435.     repeat
  2436.       local securePass = readInput(termX / 2, 15, yellow, black, 12)
  2437.       if securePass == dhdSettings.password then
  2438.         netSend("allclear")
  2439.         secureStatus = "allclear"
  2440.         drawElement(15, 9, termX - 14, 1, nil, nil, "")  --# clear !!LOCKDOWN!!
  2441.         drawElement(15, 15, termX - 14, 1, nil, nil, "") --# clear password area
  2442.         if oldOsPullEvent then os.pullEvent = oldOsPullEvent end
  2443.         oldOsPullEvent = nil
  2444.         waitingForIris = true
  2445.         if displayState == "list" then
  2446.           displayStatus()
  2447.           displayAddressBook()
  2448.         else
  2449.           displayNotes(selectedGate)
  2450.         end
  2451.         states[runState]()
  2452.       end
  2453.     until secureStatus == "allclear"
  2454.   end
  2455. end
  2456.  
  2457. local function dhdKernel()
  2458.   repeat
  2459.     if kernelState and secureStatus == "lockdown" then
  2460.       parallel.waitForAny(secKernel, bioScan, monTouch, netReceive)
  2461.     elseif kernelState and secureStatus == "allclear" then
  2462.       if runState == "Dial" or runState == "Trim" or runState == "DHDhelp" or runState == "logs" then
  2463.         parallel.waitForAny(mClick, mScroll, keyClick, bioScan, monTouch, netReceive)
  2464.       elseif runState == "DHDsettings" then
  2465.         parallel.waitForAny(editSettings, bioScan, monTouch, netReceive)
  2466.       elseif runState == "clearLogs" then
  2467.         parallel.waitForAny(clearLogsPopUp, bioScan, monTouch, netReceive)
  2468.       elseif runState == "importExport" then
  2469.         parallel.waitForAny(importExportPopUp, bioScan, monTouch, netReceive)
  2470.       elseif runState == "goPage" or runState == "goTrimPage" then
  2471.         parallel.waitForAny(goToPage, bioScan, monTouch, netReceive)
  2472.       elseif runState == "goLogPage" then
  2473.         parallel.waitForAny(goToLogPage, bioScan, monTouch, netReceive)
  2474.       elseif runState == "newPassword" then
  2475.         parallel.waitForAny(changeLockdownPassword, bioScan, monTouch, netReceive)
  2476.       elseif runState == "GateEdit" then
  2477.         parallel.waitForAny(editGate, mScroll, bioScan, monTouch, netReceive)
  2478.       elseif runState == "exodus" then
  2479.         parallel.waitForAny(saveAndQuitPopUp, bioScan, monTouch, netReceive)
  2480.       end
  2481.     end
  2482.   until not kernelState
  2483. end
  2484.  
  2485. local function missingComponent(id)
  2486.   clearScreen()
  2487.   if id == "modem" then
  2488.     drawElement(2, 2, 1, 1, red, black, "No wireless modem detected!")
  2489.     drawElement(2, 4, 1, 1, gray, nil, "ccDHD REQUIRES a wireless modem.")
  2490.   end
  2491.   term.setCursorPos(1, 8)
  2492.   kernelState = false
  2493.   return false
  2494. end
  2495.  
  2496. local function addMonitor(attachName)
  2497.   peripheral.call(attachName, "setTextScale", 1)
  2498.   local monX = peripheral.call(attachName, "getSize")
  2499.   if monX == 7 then
  2500.     hardware.listMon = hardware.listMon + 1
  2501.     listMon[hardware.listMon] = peripheral.wrap(attachName)
  2502.     listMonSides[hardware.listMon] = attachName
  2503.   elseif monX == 29 then
  2504.     hardware.marquee = hardware.marquee + 1
  2505.     marquee[hardware.marquee] = peripheral.wrap(attachName)
  2506.     marqueeSides[hardware.marquee] = attachName
  2507.   end
  2508. end
  2509.  
  2510. local function detectHardware(hwType, firstRunNow)
  2511.   if hwType == "bio" then
  2512.     hardware.bio = 0
  2513.     local tempBio = { peripheral.find("biolock") }
  2514.     hardware.bio = #tempBio
  2515.     if hardware.bio > 0 then
  2516.       drawElement(16, 8, 1, 1, green, gray, "O")
  2517.     else
  2518.       drawElement(16, 8, 1, 1, red, nil, "0")
  2519.       dhdSettings.bio.lock = false --# This accounts for a scanner removed between sessions
  2520.     end
  2521.     return
  2522.   elseif hwType == "modem" then
  2523.     hardware.modemSide = "none"
  2524.     for _, side in ipairs(rs.getSides()) do
  2525.       if peripheral.isPresent(side) and peripheral.getType(side) == "modem" and peripheral.call(side, "isWireless") then
  2526.         hardware.modemSide = side
  2527.         rednet.open(hardware.modemSide)
  2528.         if runState == "init" and not firstRunNow then
  2529.           drawElement(16, 6, 1, 1, green, gray, "O")     --# Hardware Discovery complete
  2530.           drawElement(16, 9, 1, 1, nil, nil, "O")        --# WiFi
  2531.         end
  2532.         break
  2533.       end
  2534.     end
  2535.     return
  2536.   elseif hwType == "mon" then
  2537.     hardware.listMon = 0
  2538.     hardware.marquee = 0
  2539.     for _, side in ipairs(rs.getSides()) do
  2540.       if peripheral.isPresent(side) then
  2541.         if peripheral.getType(side) == "monitor" and peripheral.call(side, "isColor") then
  2542.           addMonitor(side)
  2543.         elseif peripheral.getType(side) == "modem" and not peripheral.call(side, "isWireless") then
  2544.           local mPerps = peripheral.call(side, "getNamesRemote")
  2545.           for _, name in ipairs(mPerps) do
  2546.             if name:sub(1, 7) == "monitor" and peripheral.call(name, "isColor") then
  2547.               addMonitor(name)
  2548.             end
  2549.           end
  2550.         end
  2551.       end
  2552.     end
  2553.   end
  2554. end
  2555.  
  2556. local function waitingAnimation() --# intentionally not using drawElement - less expense
  2557.   while true do
  2558.     for i = 9, 25 do
  2559.       term.setCursorPos(i, 5)
  2560.       term.setBackgroundColor(cyan)
  2561.       term.write(" ")
  2562.       term.setBackgroundColor(black)
  2563.       term.setCursorPos(i - 1, 5)
  2564.       term.write(" ")
  2565.       sleep(0.04)
  2566.     end
  2567.     for i = 25, 9, -1 do
  2568.       term.setCursorPos(i, 5)
  2569.       term.setBackgroundColor(cyan)
  2570.       term.write(" ")
  2571.       term.setBackgroundColor(black)
  2572.       term.setCursorPos(i + 1, 5)
  2573.       term.write(" ")
  2574.       sleep(0.04)
  2575.     end
  2576.   end
  2577. end
  2578.  
  2579. local function waitingNet()
  2580.   while true do
  2581.     local gateLiaison = rednet.lookup("ccDHDSetup")
  2582.     if gateLiaison then
  2583.       dhdSettings.gate = gateLiaison
  2584.       netSend("1stRun")
  2585.       while true do
  2586.         local id, message = rednet.receive()
  2587.         if id == dhdSettings.gate and type(message) == "table" then
  2588.           if message.program == "ccDHD" then
  2589.             thisGate = message.thisGate
  2590.             return
  2591.           end
  2592.         end
  2593.       end
  2594.     else
  2595.       sleep(1)
  2596.     end
  2597.   end
  2598. end
  2599.  
  2600. local function firstRun()
  2601.   drawElement(2, 4, 1, 1, gray, black, "... waiting for gateLiaison ...")
  2602.   parallel.waitForAny(waitingNet, waitingAnimation)
  2603.   --# Check and, if necessary, set computer label
  2604.   local ccLabel = os.getComputerLabel()
  2605.   if ccLabel == nil or tostring(ccLabel) == "" or ccLabel == "DHD" then
  2606.     os.setComputerLabel(thisGate .. " DHD")
  2607.   end
  2608.   clearScreen()
  2609.   saveData(settingsData, "cfg")
  2610.   return true
  2611. end
  2612.  
  2613. local function initMe()
  2614.   clearScreen()
  2615.   local firstRunNow = false
  2616.   if not fs.exists(settingsData) then
  2617.     if not fs.exists("/data") then fs.makeDir("/data") end
  2618.     firstRunNow = true
  2619.     detectHardware("modem", true)
  2620.     if hardware.modemSide == "none" then return missingComponent("modem") end
  2621.     if not firstRun() then return false end
  2622.   end
  2623.   drawElement(15, 4, 22, 1, white, blue, "ccDHD Initializing")
  2624.   drawElement(15, 5, 22, 10, nil, gray, "")
  2625.   drawElement(18, 6, 1, 1, silver, nil, "Hardware Discovery")
  2626.   drawElement(18, 7, 1, 1, nil, nil, "Monitors")
  2627.   drawElement(18, 8, 1, 1, nil, nil, "Biometric Scanner")
  2628.   drawElement(18, 9, 1, 1, nil, nil, "Wireless Modem")
  2629.  
  2630.   drawElement(18, 10, 1, 1, nil, nil, "Query gateLiaison")
  2631.   drawElement(18, 11, 1, 1, nil, nil, "Await response")
  2632.   drawElement(18, 12, 1, 1, nil, nil, "Host pSync")
  2633.  
  2634.   drawElement(18, 13, 1, 1, nil, nil, "Ingest gate data")
  2635.   drawElement(16, 6, 1, 1, red, nil, "0")        --# Hardware Discovery
  2636.   drawElement(16, 7, 1, 1, nil, nil, "0")        --# Monitors
  2637.   drawElement(16, 8, 1, 1, nil, nil, "0")        --# Biolocks
  2638.   drawElement(16, 9, 1, 1, nil, nil, "0")        --# WiFi
  2639.  
  2640.   drawElement(16, 10, 1, 1, nil, nil, "0")       --# Query gateLiaison
  2641.   drawElement(16, 11, 1, 1, nil, nil, "0")       --# Await response
  2642.   drawElement(16, 12, 1, 1, nil, nil, "0")       --# Host pSync
  2643.  
  2644.   drawElement(16, 13, 1, 1, nil, nil, "0")       --# Ingest gate data
  2645.   if not firstRunNow then ingestData(settingsData, "cfg") end
  2646.   drawElement(18, 6, 1, 1, white, nil, "Hardware Discovery")
  2647.   drawElement(18, 7, 1, 1, nil, nil, "Monitors")
  2648.   drawElement(16, 6, 1, 1, orange, nil, "0")     --# Hardware Discovery
  2649.   drawElement(16, 7, 1, 1, nil, nil, "0")        --# Monitors
  2650.   detectHardware("mon")
  2651.   if hardware.listMon > 0 or hardware.marquee > 0 then
  2652.     drawElement(16, 7, 1, 1, green, nil, "O")    --# Monitors
  2653.   else
  2654.     drawElement(16, 7, 1, 1, red, nil, "0")
  2655.   end
  2656.   if hardware.listMon > 0 then
  2657.     for i = 1, hardware.listMon do
  2658.       listMon[i].setBackgroundColor(white)
  2659.       listMon[i].setTextColor(black)
  2660.       listMon[i].clear()
  2661.       listMon[i].setTextScale(1)
  2662.       listMon[i].setCursorPos(1, 3)
  2663.       listMon[i].write("Init...")
  2664.     end
  2665.   end
  2666.   if hardware.marquee > 0 then
  2667.     for i = 1, hardware.marquee do
  2668.       marquee[i].setBackgroundColor(white)
  2669.       marquee[i].setTextColor(black)
  2670.       marquee[i].clear()
  2671.       marquee[i].setTextScale(2)
  2672.       marquee[i].setCursorPos(5, 1)
  2673.       marquee[i].write("ccDHD")
  2674.       marquee[i].setCursorPos(2, 2)
  2675.       marquee[i].write("Initializing")
  2676.     end
  2677.   end
  2678.   drawElement(16, 8, 1, 1, orange, nil, "0")     --# Biolocks
  2679.   drawElement(18, 8, 1, 1, white, nil, "Biometric Scanner")
  2680.   detectHardware("bio")
  2681.   drawElement(18, 9, 1, 1, white, nil, "Wireless Modem")
  2682.   if firstRunNow then
  2683.     drawElement(16, 6, 1, 1, green, nil, "O")    --# Hardware Discovery complete
  2684.     drawElement(16, 9, 1, 1, nil, nil, "O")      --# WiFi
  2685.   else
  2686.     detectHardware("modem")
  2687.   end
  2688.   if hardware.modemSide == "none" then shutDown() return missingComponent("modem") end
  2689.   drawElement(16, 10, 1, 1, orange, nil, "0")    --# Query gateLiaison
  2690.   drawElement(18, 10, 1, 1, white, nil, "Query gateLiaison")
  2691.   netSend("QRY")
  2692.   drawElement(16, 10, 1, 1, green, nil, "O")     --# Query gateLiaison
  2693.   drawElement(16, 11, 1, 1, orange, nil, "0")    --# Await response
  2694.   drawElement(18, 11, 1, 1, white, nil, "Await response")
  2695.   local missed = 0
  2696.   while true do
  2697.     local id, message = rednet.receive(1)
  2698.     if id == dhdSettings.gate then
  2699.       updateStatus(message)
  2700.       drawElement(16, 11, 1, 1, green, nil, "O") --# Await response
  2701.       break
  2702.     end
  2703.     missed = missed + 1
  2704.     if missed >= 10 then
  2705.       shutDown()
  2706.       clearScreen()
  2707.       drawElement(2, 2, 1, 1, red, nil, "Unable to locate gateLiaison")
  2708.       term.setCursorPos(1, 4)
  2709.       return false
  2710.     end
  2711.   end
  2712.   drawElement(18, 12, 1, 1, white, nil, "Host pSync")
  2713.   if dhdSettings.pSync then
  2714.     drawElement(16, 12, 1, 1, orange, nil, "0")  --# Host pSync
  2715.     rednet.host("pSync", thisGate)
  2716.     drawElement(16, 12, 1, 1, green, nil, "O")   --# Host pSync
  2717.   end
  2718.   drawElement(16, 13, 1, 1, orange, nil, "0")    --# Ingest gate data
  2719.   drawElement(18, 13, 1, 1, white, nil, "Ingest gate data")
  2720.   if fs.exists(gateData) then
  2721.     ingestData(gateData, "gate")
  2722.   else
  2723.     if not fs.exists("/data") then fs.makeDir("/data") end
  2724.     addressBook[1].name = thisGate
  2725.     addressBook[1].addr = thisGate
  2726.     saveData(gateData, "gate")
  2727.   end
  2728.   drawElement(16, 13, 1, 1, green, nil, "O")     --# Ingest gate data
  2729.   sleep(0.1)
  2730.   clearMonitors()
  2731.   if dhdSettings.bio.lock and hardware.bio > 0 then --# Bioscanner login
  2732.     bioLogin()
  2733.   end
  2734.   kernelState = true
  2735.   runState = "Dial"
  2736.   clearScreen()
  2737.   drawCLI(true)
  2738.   return true
  2739. end
  2740.  
  2741. if not initMe() then return end
  2742.  
  2743. dhdKernel()
  2744. shutDown()
  2745. clearScreen()
  2746. term.setTextColor(white)
  2747. term.setCursorPos(1, 1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement