Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[ LanteaCraft ]]--
- --[[ and SGCraft ]]--
- --[[ ccDialer ]]--
- --[[ by Dog ]]--
- --[[ aka HydrantHunter ]]--
- --[[ for Plethora ]]--
- --[[ by SquidDev ]]--
- --[[ pastebin VgUMbZyF ]]--
- local ccDialVer = "2.0.00"
- --[[
- Tested with/requires:
- - Minecraft 1.7.10+ AND ComputerCraft 1.75+ || LanteaCraft LC2-16+ | OR | SGCraft1.11.x-mc1.7.10+
- For setup and usage instructions, please visit http://tinyurl.com/jf3rjr7
- Special thanks to: theoriginalbit (custom read function)
- Anavrins (pbkdf2/sha256 hashing)
- SquidDev (AES encryption/decryption)
- Alex Kloss (base64 encoder/decoder)
- IMPORTANT NOTE:
- - all of the following variables are set by the program as necessary
- - editing any of them below will most likely cause unexpected results
- ]]--
- --# AUTOMATIC/STATIC CONFIGURATION
- --# Peripherals
- local termX, termY = term.getSize() --# 39 x 13 (39 x 12 tab)
- local modemSide, thisCC = "none", tostring(os.getComputerID())
- --# Status Info
- local runState, dialAddress, remoteIrisState = "init", "none", "unk"
- local drawCLI, drawAddressBook, drawElement, pNum, currentEdit, word, curX, curY, bgCol, txtCol, pingTimer
- local missedPings = 0
- local hosts, host = { }, { id = nil, name = "NO HOST", addr = "NO HOST", ccDHD = false, status = nil }
- local gettingInput = false
- --# Address Book
- local addressBook = { { name = "NO GATES", addr = "ADDRESS", rating = "U", iris = "none", callDrop = false, note = "Short note", loc = { x = 0, y = 0, z = 0, dim = "Unspecified", }, }, }
- local gateChange = false
- local gatePages, gatePage, syncPages, syncPage, abCount = 1, 1, 1, 1, 1
- local classifications = {
- B = { order = 1, color = colors.blue, label = "Base/Outpost/Hub" };
- H = { order = 2, color = colors.lightBlue, label = "Home/Camp" };
- V = { order = 3, color = colors.brown, label = "Village" };
- M = { order = 4, color = colors.purple, label = "Misc/Special" };
- S = { order = 5, color = colors.green, label = "Safe/Secured" };
- C = { order = 6, color = colors.orange, label = "Caution" };
- D = { order = 7, color = colors.red, label = "Danger" };
- U = { order = 8, color = colors.lightGray, label = "Unk/Unclassified", long = "Unknown/Unclassified" };
- }
- --# Color Definitions
- local white = colors.white
- local silver = colors.lightGray
- local gray = colors.gray
- local black = colors.black
- local brown = colors.brown
- local yellow = colors.yellow
- local orange = colors.orange
- local red = colors.red
- --local magenta = colors.magenta
- --local purple = colors.purple
- local blue = colors.blue
- local sky = colors.lightBlue
- local cyan = colors.cyan
- --local lime = colors.lime
- local green = colors.green
- --# END AUTOMATIC/STATIC CONFIGURATION
- -- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
- -- licensed under the terms of the LGPL2
- -- http://lua-users.org/wiki/BaseSixtyFour
- -- character table string
- local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
- -- encoding
- local function encode(data)
- return ((data:gsub('.', function(x)
- local r,b='',x:byte()
- for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
- return r;
- end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
- if (#x < 6) then return '' end
- local c=0
- for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
- return b:sub(c+1,c+1)
- end)..({ '', '==', '=' })[#data%3+1])
- end
- -- decoding
- local function decode(data)
- data = string.gsub(data, '[^'..b..'=]', '')
- return (data:gsub('.', function(x)
- if (x == '=') then return '' end
- local r,f='',(b:find(x)-1)
- for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
- return r;
- end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
- if (#x ~= 8) then return '' end
- local c=0
- for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
- return string.char(c)
- end))
- end
- -- AES Lua implementation by SquidDev
- -- https://gist.github.com/SquidDev/86925e07cbabd70773e53d781bd8b2fe
- local encrypt, decrypt
- do
- local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
- local bit=_W(function(_ENV, ...)
- --[[
- This bit API is designed to cope with unsigned integers instead of normal integers
- To do this we add checks for overflows: (x > 2^31 ? x - 2 ^ 32 : x)
- These are written in long form because no constant folding.
- ]]
- local floor = math.floor
- local lshift, rshift
- rshift = function(a,disp)
- return floor(a % 4294967296 / 2^disp)
- end
- lshift = function(a,disp)
- return (a * 2^disp) % 4294967296
- end
- return {
- -- bit operations
- bnot = bit32 and bit32.bnot or bit.bnot,
- band = bit32 and bit32.band or bit.band,
- bor = bit32 and bit32.bor or bit.bor,
- bxor = bit32 and bit32.bxor or bit.bxor,
- rshift = rshift,
- lshift = lshift,
- }
- end)
- local gf=_W(function(_ENV, ...)
- -- finite field with base 2 and modulo irreducible polynom x^8+x^4+x^3+x+1 = 0x11d
- local bxor = bit32 and bit32.bxor or bit.bxor
- local lshift = bit.lshift
- -- private data of gf
- local n = 0x100
- local ord = 0xff
- local irrPolynom = 0x11b
- local exp, log = {}, {}
- --
- -- add two polynoms (its simply xor)
- --
- local function add(operand1, operand2)
- return bxor(operand1,operand2)
- end
- --
- -- subtract two polynoms (same as addition)
- --
- local function sub(operand1, operand2)
- return bxor(operand1,operand2)
- end
- --
- -- inverts element
- -- a^(-1) = g^(order - log(a))
- --
- local function invert(operand)
- -- special case for 1 or normal invert
- return operand == 1 and 1 or exp[ord - log[operand]]
- end
- --
- -- multiply two elements using a logarithm table
- -- a*b = g^(log(a)+log(b))
- --
- local function mul(operand1, operand2)
- if (operand1 == 0 or operand2 == 0) then
- return 0
- end
- local exponent = log[operand1] + log[operand2]
- if (exponent >= ord) then
- exponent = exponent - ord
- end
- return exp[exponent]
- end
- --
- -- divide two elements
- -- a/b = g^(log(a)-log(b))
- --
- local function div(operand1, operand2)
- if (operand1 == 0) then
- return 0
- end
- -- TODO: exception if operand2 == 0
- local exponent = log[operand1] - log[operand2]
- if (exponent < 0) then
- exponent = exponent + ord
- end
- return exp[exponent]
- end
- --
- -- print logarithmic table
- --
- local function printLog()
- for i = 1, n do
- print("log(", i-1, ")=", log[i-1])
- end
- end
- --
- -- print exponentiation table
- --
- local function printExp()
- for i = 1, n do
- print("exp(", i-1, ")=", exp[i-1])
- end
- end
- --
- -- calculate logarithmic and exponentiation table
- --
- local function initMulTable()
- local a = 1
- for i = 0,ord-1 do
- exp[i] = a
- log[a] = i
- -- multiply with generator x+1 -> left shift + 1
- a = bxor(lshift(a, 1), a)
- -- if a gets larger than order, reduce modulo irreducible polynom
- if a > ord then
- a = sub(a, irrPolynom)
- end
- end
- end
- initMulTable()
- return {
- add = add,
- sub = sub,
- invert = invert,
- mul = mul,
- div = div,
- printLog = printLog,
- printExp = printExp,
- }
- end)
- util=_W(function(_ENV, ...)
- -- Cache some bit operators
- local bxor = bit.bxor
- local rshift = bit.rshift
- local band = bit.band
- local lshift = bit.lshift
- local sleepCheckIn
- --
- -- calculate the parity of one byte
- --
- local function byteParity(byte)
- byte = bxor(byte, rshift(byte, 4))
- byte = bxor(byte, rshift(byte, 2))
- byte = bxor(byte, rshift(byte, 1))
- return band(byte, 1)
- end
- --
- -- get byte at position index
- --
- local function getByte(number, index)
- return index == 0 and band(number,0xff) or band(rshift(number, index*8),0xff)
- end
- --
- -- put number into int at position index
- --
- local function putByte(number, index)
- return index == 0 and band(number,0xff) or lshift(band(number,0xff),index*8)
- end
- --
- -- convert byte array to int array
- --
- local function bytesToInts(bytes, start, n)
- local ints = {}
- for i = 0, n - 1 do
- ints[i + 1] =
- putByte(bytes[start + (i*4)], 3) +
- putByte(bytes[start + (i*4) + 1], 2) +
- putByte(bytes[start + (i*4) + 2], 1) +
- putByte(bytes[start + (i*4) + 3], 0)
- if n % 10000 == 0 then sleepCheckIn() end
- end
- return ints
- end
- --
- -- convert int array to byte array
- --
- local function intsToBytes(ints, output, outputOffset, n)
- n = n or #ints
- for i = 0, n - 1 do
- for j = 0,3 do
- output[outputOffset + i*4 + (3 - j)] = getByte(ints[i + 1], j)
- end
- if n % 10000 == 0 then sleepCheckIn() end
- end
- return output
- end
- --
- -- convert bytes to hexString
- --
- local function bytesToHex(bytes)
- local hexBytes = ""
- for i,byte in ipairs(bytes) do
- hexBytes = hexBytes .. string.format("%02x ", byte)
- end
- return hexBytes
- end
- local function hexToBytes(bytes)
- local out = {}
- for i = 1, #bytes, 2 do
- out[#out + 1] = tonumber(bytes:sub(i, i + 1), 16)
- end
- return out
- end
- --
- -- convert data to hex string
- --
- local function toHexString(data)
- local type = type(data)
- if (type == "number") then
- return string.format("%08x",data)
- elseif (type == "table") then
- return bytesToHex(data)
- elseif (type == "string") then
- local bytes = {string.byte(data, 1, #data)}
- return bytesToHex(bytes)
- else
- return data
- end
- end
- local function padByteString(data)
- local dataLength = #data
- local random1 = math.random(0,255)
- local random2 = math.random(0,255)
- local prefix = string.char(random1,
- random2,
- random1,
- random2,
- getByte(dataLength, 3),
- getByte(dataLength, 2),
- getByte(dataLength, 1),
- getByte(dataLength, 0)
- )
- data = prefix .. data
- local padding, paddingLength = "", math.ceil(#data/16)*16 - #data
- for i=1,paddingLength do
- padding = padding .. string.char(math.random(0,255))
- end
- return data .. padding
- end
- local function properlyDecrypted(data)
- local random = {string.byte(data,1,4)}
- if (random[1] == random[3] and random[2] == random[4]) then
- return true
- end
- return false
- end
- local function unpadByteString(data)
- if (not properlyDecrypted(data)) then
- return nil
- end
- local dataLength = putByte(string.byte(data,5), 3)
- + putByte(string.byte(data,6), 2)
- + putByte(string.byte(data,7), 1)
- + putByte(string.byte(data,8), 0)
- return string.sub(data,9,8+dataLength)
- end
- local function xorIV(data, iv)
- for i = 1,16 do
- data[i] = bxor(data[i], iv[i])
- end
- end
- local function increment(data)
- local i = 16
- while true do
- local value = data[i] + 1
- if value >= 256 then
- data[i] = value - 256
- i = (i - 2) % 16 + 1
- else
- data[i] = value
- break
- end
- end
- end
- -- Called every encryption cycle
- local push, pull, time = os.queueEvent, coroutine.yield, os.time
- local oldTime = time()
- local function sleepCheckIn()
- local newTime = time()
- if newTime - oldTime >= 0.03 then -- (0.020 * 1.5)
- oldTime = newTime
- push("sleep")
- pull("sleep")
- end
- end
- local function getRandomData(bytes)
- local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
- local result = {}
- for i=1,bytes do
- insert(result, random(0,255))
- if i % 10240 == 0 then sleep() end
- end
- return result
- end
- local function getRandomString(bytes)
- local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
- local result = {}
- for i=1,bytes do
- insert(result, char(random(0,255)))
- if i % 10240 == 0 then sleep() end
- end
- return table.concat(result)
- end
- return {
- byteParity = byteParity,
- getByte = getByte,
- putByte = putByte,
- bytesToInts = bytesToInts,
- intsToBytes = intsToBytes,
- bytesToHex = bytesToHex,
- hexToBytes = hexToBytes,
- toHexString = toHexString,
- padByteString = padByteString,
- properlyDecrypted = properlyDecrypted,
- unpadByteString = unpadByteString,
- xorIV = xorIV,
- increment = increment,
- sleepCheckIn = sleepCheckIn,
- getRandomData = getRandomData,
- getRandomString = getRandomString,
- }
- end)
- aes=_W(function(_ENV, ...)
- -- Implementation of AES with nearly pure lua
- -- AES with lua is slow, really slow :-)
- local putByte = util.putByte
- local getByte = util.getByte
- -- some constants
- local ROUNDS = 'rounds'
- local KEY_TYPE = "type"
- local ENCRYPTION_KEY=1
- local DECRYPTION_KEY=2
- -- aes SBOX
- local SBox = {}
- local iSBox = {}
- -- aes tables
- local table0 = {}
- local table1 = {}
- local table2 = {}
- local table3 = {}
- local tableInv0 = {}
- local tableInv1 = {}
- local tableInv2 = {}
- local tableInv3 = {}
- -- round constants
- local rCon = {
- 0x01000000,
- 0x02000000,
- 0x04000000,
- 0x08000000,
- 0x10000000,
- 0x20000000,
- 0x40000000,
- 0x80000000,
- 0x1b000000,
- 0x36000000,
- 0x6c000000,
- 0xd8000000,
- 0xab000000,
- 0x4d000000,
- 0x9a000000,
- 0x2f000000,
- }
- --
- -- affine transformation for calculating the S-Box of AES
- --
- local function affinMap(byte)
- mask = 0xf8
- result = 0
- for i = 1,8 do
- result = bit.lshift(result,1)
- parity = util.byteParity(bit.band(byte,mask))
- result = result + parity
- -- simulate roll
- lastbit = bit.band(mask, 1)
- mask = bit.band(bit.rshift(mask, 1),0xff)
- mask = lastbit ~= 0 and bit.bor(mask, 0x80) or bit.band(mask, 0x7f)
- end
- return bit.bxor(result, 0x63)
- end
- --
- -- calculate S-Box and inverse S-Box of AES
- -- apply affine transformation to inverse in finite field 2^8
- --
- local function calcSBox()
- for i = 0, 255 do
- inverse = i ~= 0 and gf.invert(i) or 0
- mapped = affinMap(inverse)
- SBox[i] = mapped
- iSBox[mapped] = i
- end
- end
- --
- -- Calculate round tables
- -- round tables are used to calculate shiftRow, MixColumn and SubBytes
- -- with 4 table lookups and 4 xor operations.
- --
- local function calcRoundTables()
- for x = 0,255 do
- byte = SBox[x]
- table0[x] = putByte(gf.mul(0x03, byte), 0)
- + putByte( byte , 1)
- + putByte( byte , 2)
- + putByte(gf.mul(0x02, byte), 3)
- table1[x] = putByte( byte , 0)
- + putByte( byte , 1)
- + putByte(gf.mul(0x02, byte), 2)
- + putByte(gf.mul(0x03, byte), 3)
- table2[x] = putByte( byte , 0)
- + putByte(gf.mul(0x02, byte), 1)
- + putByte(gf.mul(0x03, byte), 2)
- + putByte( byte , 3)
- table3[x] = putByte(gf.mul(0x02, byte), 0)
- + putByte(gf.mul(0x03, byte), 1)
- + putByte( byte , 2)
- + putByte( byte , 3)
- end
- end
- --
- -- Calculate inverse round tables
- -- does the inverse of the normal roundtables for the equivalent
- -- decryption algorithm.
- --
- local function calcInvRoundTables()
- for x = 0,255 do
- byte = iSBox[x]
- tableInv0[x] = putByte(gf.mul(0x0b, byte), 0)
- + putByte(gf.mul(0x0d, byte), 1)
- + putByte(gf.mul(0x09, byte), 2)
- + putByte(gf.mul(0x0e, byte), 3)
- tableInv1[x] = putByte(gf.mul(0x0d, byte), 0)
- + putByte(gf.mul(0x09, byte), 1)
- + putByte(gf.mul(0x0e, byte), 2)
- + putByte(gf.mul(0x0b, byte), 3)
- tableInv2[x] = putByte(gf.mul(0x09, byte), 0)
- + putByte(gf.mul(0x0e, byte), 1)
- + putByte(gf.mul(0x0b, byte), 2)
- + putByte(gf.mul(0x0d, byte), 3)
- tableInv3[x] = putByte(gf.mul(0x0e, byte), 0)
- + putByte(gf.mul(0x0b, byte), 1)
- + putByte(gf.mul(0x0d, byte), 2)
- + putByte(gf.mul(0x09, byte), 3)
- end
- end
- --
- -- rotate word: 0xaabbccdd gets 0xbbccddaa
- -- used for key schedule
- --
- local function rotWord(word)
- local tmp = bit.band(word,0xff000000)
- return (bit.lshift(word,8) + bit.rshift(tmp,24))
- end
- --
- -- replace all bytes in a word with the SBox.
- -- used for key schedule
- --
- local function subWord(word)
- return putByte(SBox[getByte(word,0)],0)
- + putByte(SBox[getByte(word,1)],1)
- + putByte(SBox[getByte(word,2)],2)
- + putByte(SBox[getByte(word,3)],3)
- end
- --
- -- generate key schedule for aes encryption
- --
- -- returns table with all round keys and
- -- the necessary number of rounds saved in [ROUNDS]
- --
- local function expandEncryptionKey(key)
- local keySchedule = {}
- local keyWords = math.floor(#key / 4)
- if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then
- error("Invalid key size: " .. tostring(keyWords))
- return nil
- end
- keySchedule[ROUNDS] = keyWords + 6
- keySchedule[KEY_TYPE] = ENCRYPTION_KEY
- for i = 0,keyWords - 1 do
- keySchedule[i] = putByte(key[i*4+1], 3)
- + putByte(key[i*4+2], 2)
- + putByte(key[i*4+3], 1)
- + putByte(key[i*4+4], 0)
- end
- for i = keyWords, (keySchedule[ROUNDS] + 1)*4 - 1 do
- local tmp = keySchedule[i-1]
- if ( i % keyWords == 0) then
- tmp = rotWord(tmp)
- tmp = subWord(tmp)
- local index = math.floor(i/keyWords)
- tmp = bit.bxor(tmp,rCon[index])
- elseif (keyWords > 6 and i % keyWords == 4) then
- tmp = subWord(tmp)
- end
- keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp)
- end
- return keySchedule
- end
- --
- -- Inverse mix column
- -- used for key schedule of decryption key
- --
- local function invMixColumnOld(word)
- local b0 = getByte(word,3)
- local b1 = getByte(word,2)
- local b2 = getByte(word,1)
- local b3 = getByte(word,0)
- return putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1),
- gf.mul(0x0d, b2)),
- gf.mul(0x09, b3)),
- gf.mul(0x0e, b0)),3)
- + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2),
- gf.mul(0x0d, b3)),
- gf.mul(0x09, b0)),
- gf.mul(0x0e, b1)),2)
- + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3),
- gf.mul(0x0d, b0)),
- gf.mul(0x09, b1)),
- gf.mul(0x0e, b2)),1)
- + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0),
- gf.mul(0x0d, b1)),
- gf.mul(0x09, b2)),
- gf.mul(0x0e, b3)),0)
- end
- --
- -- Optimized inverse mix column
- -- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
- -- TODO: make it work
- --
- local function invMixColumn(word)
- local b0 = getByte(word,3)
- local b1 = getByte(word,2)
- local b2 = getByte(word,1)
- local b3 = getByte(word,0)
- local t = bit.bxor(b3,b2)
- local u = bit.bxor(b1,b0)
- local v = bit.bxor(t,u)
- v = bit.bxor(v,gf.mul(0x08,v))
- w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0)))
- v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1)))
- return putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0)
- + putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t )), 1)
- + putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2)
- + putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u )), 3)
- end
- --
- -- generate key schedule for aes decryption
- --
- -- uses key schedule for aes encryption and transforms each
- -- key by inverse mix column.
- --
- local function expandDecryptionKey(key)
- local keySchedule = expandEncryptionKey(key)
- if (keySchedule == nil) then
- return nil
- end
- keySchedule[KEY_TYPE] = DECRYPTION_KEY
- for i = 4, (keySchedule[ROUNDS] + 1)*4 - 5 do
- keySchedule[i] = invMixColumnOld(keySchedule[i])
- end
- return keySchedule
- end
- --
- -- xor round key to state
- --
- local function addRoundKey(state, key, round)
- for i = 0, 3 do
- state[i + 1] = bit.bxor(state[i + 1], key[round*4+i])
- end
- end
- --
- -- do encryption round (ShiftRow, SubBytes, MixColumn together)
- --
- local function doRound(origState, dstState)
- dstState[1] = bit.bxor(bit.bxor(bit.bxor(
- table0[getByte(origState[1],3)],
- table1[getByte(origState[2],2)]),
- table2[getByte(origState[3],1)]),
- table3[getByte(origState[4],0)])
- dstState[2] = bit.bxor(bit.bxor(bit.bxor(
- table0[getByte(origState[2],3)],
- table1[getByte(origState[3],2)]),
- table2[getByte(origState[4],1)]),
- table3[getByte(origState[1],0)])
- dstState[3] = bit.bxor(bit.bxor(bit.bxor(
- table0[getByte(origState[3],3)],
- table1[getByte(origState[4],2)]),
- table2[getByte(origState[1],1)]),
- table3[getByte(origState[2],0)])
- dstState[4] = bit.bxor(bit.bxor(bit.bxor(
- table0[getByte(origState[4],3)],
- table1[getByte(origState[1],2)]),
- table2[getByte(origState[2],1)]),
- table3[getByte(origState[3],0)])
- end
- --
- -- do last encryption round (ShiftRow and SubBytes)
- --
- local function doLastRound(origState, dstState)
- dstState[1] = putByte(SBox[getByte(origState[1],3)], 3)
- + putByte(SBox[getByte(origState[2],2)], 2)
- + putByte(SBox[getByte(origState[3],1)], 1)
- + putByte(SBox[getByte(origState[4],0)], 0)
- dstState[2] = putByte(SBox[getByte(origState[2],3)], 3)
- + putByte(SBox[getByte(origState[3],2)], 2)
- + putByte(SBox[getByte(origState[4],1)], 1)
- + putByte(SBox[getByte(origState[1],0)], 0)
- dstState[3] = putByte(SBox[getByte(origState[3],3)], 3)
- + putByte(SBox[getByte(origState[4],2)], 2)
- + putByte(SBox[getByte(origState[1],1)], 1)
- + putByte(SBox[getByte(origState[2],0)], 0)
- dstState[4] = putByte(SBox[getByte(origState[4],3)], 3)
- + putByte(SBox[getByte(origState[1],2)], 2)
- + putByte(SBox[getByte(origState[2],1)], 1)
- + putByte(SBox[getByte(origState[3],0)], 0)
- end
- --
- -- do decryption round
- --
- local function doInvRound(origState, dstState)
- dstState[1] = bit.bxor(bit.bxor(bit.bxor(
- tableInv0[getByte(origState[1],3)],
- tableInv1[getByte(origState[4],2)]),
- tableInv2[getByte(origState[3],1)]),
- tableInv3[getByte(origState[2],0)])
- dstState[2] = bit.bxor(bit.bxor(bit.bxor(
- tableInv0[getByte(origState[2],3)],
- tableInv1[getByte(origState[1],2)]),
- tableInv2[getByte(origState[4],1)]),
- tableInv3[getByte(origState[3],0)])
- dstState[3] = bit.bxor(bit.bxor(bit.bxor(
- tableInv0[getByte(origState[3],3)],
- tableInv1[getByte(origState[2],2)]),
- tableInv2[getByte(origState[1],1)]),
- tableInv3[getByte(origState[4],0)])
- dstState[4] = bit.bxor(bit.bxor(bit.bxor(
- tableInv0[getByte(origState[4],3)],
- tableInv1[getByte(origState[3],2)]),
- tableInv2[getByte(origState[2],1)]),
- tableInv3[getByte(origState[1],0)])
- end
- --
- -- do last decryption round
- --
- local function doInvLastRound(origState, dstState)
- dstState[1] = putByte(iSBox[getByte(origState[1],3)], 3)
- + putByte(iSBox[getByte(origState[4],2)], 2)
- + putByte(iSBox[getByte(origState[3],1)], 1)
- + putByte(iSBox[getByte(origState[2],0)], 0)
- dstState[2] = putByte(iSBox[getByte(origState[2],3)], 3)
- + putByte(iSBox[getByte(origState[1],2)], 2)
- + putByte(iSBox[getByte(origState[4],1)], 1)
- + putByte(iSBox[getByte(origState[3],0)], 0)
- dstState[3] = putByte(iSBox[getByte(origState[3],3)], 3)
- + putByte(iSBox[getByte(origState[2],2)], 2)
- + putByte(iSBox[getByte(origState[1],1)], 1)
- + putByte(iSBox[getByte(origState[4],0)], 0)
- dstState[4] = putByte(iSBox[getByte(origState[4],3)], 3)
- + putByte(iSBox[getByte(origState[3],2)], 2)
- + putByte(iSBox[getByte(origState[2],1)], 1)
- + putByte(iSBox[getByte(origState[1],0)], 0)
- end
- --
- -- encrypts 16 Bytes
- -- key encryption key schedule
- -- input array with input data
- -- inputOffset start index for input
- -- output array for encrypted data
- -- outputOffset start index for output
- --
- local function encrypt(key, input, inputOffset, output, outputOffset)
- --default parameters
- inputOffset = inputOffset or 1
- output = output or {}
- outputOffset = outputOffset or 1
- local state, tmpState = {}, {}
- if (key[KEY_TYPE] ~= ENCRYPTION_KEY) then
- error("No encryption key: " .. tostring(key[KEY_TYPE]) .. ", expected " .. ENCRYPTION_KEY)
- return
- end
- state = util.bytesToInts(input, inputOffset, 4)
- addRoundKey(state, key, 0)
- local round = 1
- while (round < key[ROUNDS] - 1) do
- -- do a double round to save temporary assignments
- doRound(state, tmpState)
- addRoundKey(tmpState, key, round)
- round = round + 1
- doRound(tmpState, state)
- addRoundKey(state, key, round)
- round = round + 1
- end
- doRound(state, tmpState)
- addRoundKey(tmpState, key, round)
- round = round +1
- doLastRound(tmpState, state)
- addRoundKey(state, key, round)
- util.sleepCheckIn()
- return util.intsToBytes(state, output, outputOffset)
- end
- --
- -- decrypt 16 bytes
- -- key decryption key schedule
- -- input array with input data
- -- inputOffset start index for input
- -- output array for decrypted data
- -- outputOffset start index for output
- ---
- local function decrypt(key, input, inputOffset, output, outputOffset)
- -- default arguments
- inputOffset = inputOffset or 1
- output = output or {}
- outputOffset = outputOffset or 1
- local state, tmpState = {}, {}
- if (key[KEY_TYPE] ~= DECRYPTION_KEY) then
- error("No decryption key: " .. tostring(key[KEY_TYPE]))
- return
- end
- state = util.bytesToInts(input, inputOffset, 4)
- addRoundKey(state, key, key[ROUNDS])
- local round = key[ROUNDS] - 1
- while (round > 2) do
- -- do a double round to save temporary assignments
- doInvRound(state, tmpState)
- addRoundKey(tmpState, key, round)
- round = round - 1
- doInvRound(tmpState, state)
- addRoundKey(state, key, round)
- round = round - 1
- end
- doInvRound(state, tmpState)
- addRoundKey(tmpState, key, round)
- round = round - 1
- doInvLastRound(tmpState, state)
- addRoundKey(state, key, round)
- util.sleepCheckIn()
- return util.intsToBytes(state, output, outputOffset)
- end
- -- calculate all tables when loading this file
- calcSBox()
- calcRoundTables()
- calcInvRoundTables()
- return {
- ROUNDS = ROUNDS,
- KEY_TYPE = KEY_TYPE,
- ENCRYPTION_KEY = ENCRYPTION_KEY,
- DECRYPTION_KEY = DECRYPTION_KEY,
- expandEncryptionKey = expandEncryptionKey,
- expandDecryptionKey = expandDecryptionKey,
- encrypt = encrypt,
- decrypt = decrypt,
- }
- end)
- local buffer=_W(function(_ENV, ...)
- local function new()
- return {}
- end
- local function addString(stack, s)
- table.insert(stack, s)
- end
- local function toString(stack)
- return table.concat(stack)
- end
- return {
- new = new,
- addString = addString,
- toString = toString,
- }
- end)
- ciphermode=_W(function(_ENV, ...)
- local public = {}
- --
- -- Encrypt strings
- -- key - byte array with key
- -- string - string to encrypt
- -- modefunction - function for cipher mode to use
- --
- local random, unpack = math.random, unpack or table.unpack
- function public.encryptString(key, data, modeFunction, iv)
- if iv then
- local ivCopy = {}
- for i = 1, 16 do ivCopy[i] = iv[i] end
- iv = ivCopy
- else
- iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
- end
- local keySched = aes.expandEncryptionKey(key)
- local encryptedData = buffer.new()
- for i = 1, #data/16 do
- local offset = (i-1)*16 + 1
- local byteData = {string.byte(data,offset,offset +15)}
- iv = modeFunction(keySched, byteData, iv)
- buffer.addString(encryptedData, string.char(unpack(byteData)))
- end
- return buffer.toString(encryptedData)
- end
- --
- -- the following 4 functions can be used as
- -- modefunction for encryptString
- --
- -- Electronic code book mode encrypt function
- function public.encryptECB(keySched, byteData, iv)
- aes.encrypt(keySched, byteData, 1, byteData, 1)
- end
- -- Cipher block chaining mode encrypt function
- function public.encryptCBC(keySched, byteData, iv)
- util.xorIV(byteData, iv)
- aes.encrypt(keySched, byteData, 1, byteData, 1)
- return byteData
- end
- -- Output feedback mode encrypt function
- function public.encryptOFB(keySched, byteData, iv)
- aes.encrypt(keySched, iv, 1, iv, 1)
- util.xorIV(byteData, iv)
- return iv
- end
- -- Cipher feedback mode encrypt function
- function public.encryptCFB(keySched, byteData, iv)
- aes.encrypt(keySched, iv, 1, iv, 1)
- util.xorIV(byteData, iv)
- return byteData
- end
- function public.encryptCTR(keySched, byteData, iv)
- local nextIV = {}
- for j = 1, 16 do nextIV[j] = iv[j] end
- aes.encrypt(keySched, iv, 1, iv, 1)
- util.xorIV(byteData, iv)
- util.increment(nextIV)
- return nextIV
- end
- --
- -- Decrypt strings
- -- key - byte array with key
- -- string - string to decrypt
- -- modefunction - function for cipher mode to use
- --
- function public.decryptString(key, data, modeFunction, iv)
- if iv then
- local ivCopy = {}
- for i = 1, 16 do ivCopy[i] = iv[i] end
- iv = ivCopy
- else
- iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
- end
- local keySched
- if modeFunction == public.decryptOFB or modeFunction == public.decryptCFB or modeFunction == public.decryptCTR then
- keySched = aes.expandEncryptionKey(key)
- else
- keySched = aes.expandDecryptionKey(key)
- end
- local decryptedData = buffer.new()
- for i = 1, #data/16 do
- local offset = (i-1)*16 + 1
- local byteData = {string.byte(data,offset,offset +15)}
- iv = modeFunction(keySched, byteData, iv)
- buffer.addString(decryptedData, string.char(unpack(byteData)))
- end
- return buffer.toString(decryptedData)
- end
- --
- -- the following 4 functions can be used as
- -- modefunction for decryptString
- --
- -- Electronic code book mode decrypt function
- function public.decryptECB(keySched, byteData, iv)
- aes.decrypt(keySched, byteData, 1, byteData, 1)
- return iv
- end
- -- Cipher block chaining mode decrypt function
- function public.decryptCBC(keySched, byteData, iv)
- local nextIV = {}
- for j = 1, 16 do nextIV[j] = byteData[j] end
- aes.decrypt(keySched, byteData, 1, byteData, 1)
- util.xorIV(byteData, iv)
- return nextIV
- end
- -- Output feedback mode decrypt function
- function public.decryptOFB(keySched, byteData, iv)
- aes.encrypt(keySched, iv, 1, iv, 1)
- util.xorIV(byteData, iv)
- return iv
- end
- -- Cipher feedback mode decrypt function
- function public.decryptCFB(keySched, byteData, iv)
- local nextIV = {}
- for j = 1, 16 do nextIV[j] = byteData[j] end
- aes.encrypt(keySched, iv, 1, iv, 1)
- util.xorIV(byteData, iv)
- return nextIV
- end
- public.decryptCTR = public.encryptCTR
- return public
- end)
- -- Simple API for encrypting strings.
- --
- AES128 = 16
- AES192 = 24
- AES256 = 32
- ECBMODE = 1
- CBCMODE = 2
- OFBMODE = 3
- CFBMODE = 4
- CTRMODE = 4
- local function pwToKey(password, keyLength, iv)
- local padLength = keyLength
- if (keyLength == AES192) then
- padLength = 32
- end
- if (padLength > #password) then
- local postfix = ""
- for i = 1,padLength - #password do
- postfix = postfix .. string.char(0)
- end
- password = password .. postfix
- else
- password = string.sub(password, 1, padLength)
- end
- local pwBytes = {string.byte(password,1,#password)}
- password = ciphermode.encryptString(pwBytes, password, ciphermode.encryptCBC, iv)
- password = string.sub(password, 1, keyLength)
- return {string.byte(password,1,#password)}
- end
- --
- -- Encrypts string data with password password.
- -- password - the encryption key is generated from this string
- -- data - string to encrypt (must not be too large)
- -- keyLength - length of aes key: 128(default), 192 or 256 Bit
- -- mode - mode of encryption: ecb, cbc(default), ofb, cfb
- --
- -- mode and keyLength must be the same for encryption and decryption.
- --
- function encrypt(password, data, keyLength, mode, iv)
- assert(password ~= nil, "Empty password.")
- assert(data ~= nil, "Empty data.")
- local mode = mode or CBCMODE
- local keyLength = keyLength or AES128
- local key = pwToKey(password, keyLength, iv)
- local paddedData = util.padByteString(data)
- if mode == ECBMODE then
- return ciphermode.encryptString(key, paddedData, ciphermode.encryptECB, iv)
- elseif mode == CBCMODE then
- return ciphermode.encryptString(key, paddedData, ciphermode.encryptCBC, iv)
- elseif mode == OFBMODE then
- return ciphermode.encryptString(key, paddedData, ciphermode.encryptOFB, iv)
- elseif mode == CFBMODE then
- return ciphermode.encryptString(key, paddedData, ciphermode.encryptCFB, iv)
- elseif mode == CTRMODE then
- return ciphermode.encryptString(key, paddedData, ciphermode.encryptCTR, iv)
- else
- error("Unknown mode", 2)
- end
- end
- --
- -- Decrypts string data with password password.
- -- password - the decryption key is generated from this string
- -- data - string to encrypt
- -- keyLength - length of aes key: 128(default), 192 or 256 Bit
- -- mode - mode of decryption: ecb, cbc(default), ofb, cfb
- --
- -- mode and keyLength must be the same for encryption and decryption.
- --
- function decrypt(password, data, keyLength, mode, iv)
- local mode = mode or CBCMODE
- local keyLength = keyLength or AES128
- local key = pwToKey(password, keyLength, iv)
- local plain
- if mode == ECBMODE then
- plain = ciphermode.decryptString(key, data, ciphermode.decryptECB, iv)
- elseif mode == CBCMODE then
- plain = ciphermode.decryptString(key, data, ciphermode.decryptCBC, iv)
- elseif mode == OFBMODE then
- plain = ciphermode.decryptString(key, data, ciphermode.decryptOFB, iv)
- elseif mode == CFBMODE then
- plain = ciphermode.decryptString(key, data, ciphermode.decryptCFB, iv)
- elseif mode == CTRMODE then
- plain = ciphermode.decryptString(key, data, ciphermode.decryptCTR, iv)
- else
- error("Unknown mode", 2)
- end
- result = util.unpadByteString(plain)
- if (result == nil) then
- return nil
- end
- return result
- end
- end
- -- SHA-256, HMAC and PBKDF2 functions in ComputerCraft
- -- By Anavrins
- -- For help and details, you can PM me on the CC forums
- -- http://www.computercraft.info/forums2/index.php?/user/12870-anavrins
- -- You may use this code in your projects without asking me, as long as credit is given and this header is kept intact
- -- Pastebin: http://pastebin.com/6UV4qfNF
- local digest, hmac, pbkdf2
- do
- local mod32 = 2^32
- local sha_hashlen = 32
- local sha_blocksize = 64
- local band = bit32 and bit32.band or bit.band
- local bnot = bit32 and bit32.bnot or bit.bnot
- local bxor = bit32 and bit32.bxor or bit.bxor
- local blshift = bit32 and bit32.lshift or bit.blshift
- local upack = unpack
- local function rrotate(n, b)
- local s = n/(2^b)
- local f = s%1
- return (s-f) + f*mod32
- end
- local function brshift(int, by) -- Thanks bit32 for bad rshift
- local s = int / (2^by)
- return s - s%1
- end
- local H = {
- 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
- 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
- }
- local K = {
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
- }
- local function counter(incr)
- local t1, t2 = 0, 0
- if 0xFFFFFFFF - t1 < incr then
- t2 = t2 + 1
- t1 = incr - (0xFFFFFFFF - t1) - 1
- else t1 = t1 + incr
- end
- return t2, t1
- end
- local function BE_toInt(bs, i)
- return blshift((bs[i] or 0), 24) + blshift((bs[i+1] or 0), 16) + blshift((bs[i+2] or 0), 8) + (bs[i+3] or 0)
- end
- local function preprocess(data)
- local len = #data
- local proc = {}
- data[#data+1] = 0x80
- while #data%64~=56 do data[#data+1] = 0 end
- local blocks = math.ceil(#data/64)
- for i = 1, blocks do
- proc[i] = {}
- for j = 1, 16 do
- proc[i][j] = BE_toInt(data, 1+((i-1)*64)+((j-1)*4))
- end
- end
- proc[blocks][15], proc[blocks][16] = counter(len*8)
- return proc
- end
- local function digestblock(w, C)
- for j = 17, 64 do
- local v = w[j-15]
- local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3))
- local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10))
- w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32
- end
- local a, b, c, d, e, f, g, h = upack(C)
- for j = 1, 64 do
- local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25))
- local ch = bxor(band(e, f), band(bnot(e), g))
- local temp1 = (h + S1 + ch + K[j] + w[j])%mod32
- local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22))
- local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c))
- local temp2 = (S0 + maj)%mod32
- h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32
- end
- C[1] = (C[1] + a)%mod32
- C[2] = (C[2] + b)%mod32
- C[3] = (C[3] + c)%mod32
- C[4] = (C[4] + d)%mod32
- C[5] = (C[5] + e)%mod32
- C[6] = (C[6] + f)%mod32
- C[7] = (C[7] + g)%mod32
- C[8] = (C[8] + h)%mod32
- return C
- end
- local mt = {
- __tostring = function(a) return string.char(unpack(a)) end,
- __index = {
- toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end,
- isEqual = function(self, t)
- if type(t) ~= "table" then return false end
- if #self ~= #t then return false end
- local ret = 0
- for i = 1, #self do
- ret = bit32.bor(ret, bxor(self[i], t[i]))
- end
- return ret == 0
- end
- }
- }
- local function toBytes(t, n)
- local b = {}
- for i = 1, n do
- b[(i-1)*4+1] = band(brshift(band(t[i], 0xFF000000), 24), 0xFF)
- b[(i-1)*4+2] = band(brshift(band(t[i], 0xFF0000), 16), 0xFF)
- b[(i-1)*4+3] = band(brshift(band(t[i], 0xFF00), 8), 0xFF)
- b[(i-1)*4+4] = band(t[i], 0xFF)
- end
- return setmetatable(b, mt)
- end
- digest = function(data)
- data = data or ""
- data = type(data) == "string" and {data:byte(1,-1)} or data
- data = preprocess(data)
- local C = {upack(H)}
- for i = 1, #data do C = digestblock(data[i], C) end
- return toBytes(C, 8)
- end
- hmac = function(data, key)
- local data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)}
- local key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)}
- local blocksize = sha_blocksize
- key = #key > blocksize and digest(key) or key
- local ipad = {}
- local opad = {}
- local padded_key = {}
- for i = 1, blocksize do
- ipad[i] = bxor(0x36, key[i] or 0)
- opad[i] = bxor(0x5C, key[i] or 0)
- end
- for i = 1, #data do
- ipad[blocksize+i] = data[i]
- end
- ipad = digest(ipad)
- for i = 1, blocksize do
- padded_key[i] = opad[i]
- padded_key[blocksize+i] = ipad[i]
- end
- return digest(padded_key)
- end
- pbkdf2 = function(pass, salt, iter, dklen)
- local out = {}
- local hashlen = sha_hashlen
- local block = 1
- dklen = dklen or 32
- while dklen > 0 do
- local ikey = {}
- local isalt = type(salt) == "table" and {upack(salt)} or {tostring(salt):byte(1,-1)}
- local clen = dklen > hashlen and hashlen or dklen
- local iCount = #isalt
- isalt[iCount+1] = band(brshift(band(block, 0xFF000000), 24), 0xFF)
- isalt[iCount+2] = band(brshift(band(block, 0xFF0000), 16), 0xFF)
- isalt[iCount+3] = band(brshift(band(block, 0xFF00), 8), 0xFF)
- isalt[iCount+4] = band(block, 0xFF)
- for j = 1, iter do
- isalt = hmac(isalt, pass)
- for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end
- if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end
- end
- dklen = dklen - clen
- block = block+1
- for k = 1, clen do out[#out+1] = ikey[k] end
- end
- return setmetatable(out, mt)
- end
- end
- local function mergeAddressBooks(newGates)
- local matchFound, abName, abNote, abDim, ngName, ngNote, ngDim = false --# use matchFound to indicate if a matching gate was found or not
- for i = 1, #newGates do --# start cycling through the list of 'new' gates
- for j = 1, abCount do --# search the address book for a matching address
- if newGates[i].addr == addressBook[j].addr then --# if the gate is already in the address book...
- matchFound = true --# ...set matchFound to true and...
- abName, ngName = addressBook[j].name, newGates[i].name
- if (abName == "Name" or abName == "NO GATES" or abName == "NEW GATE" or abName == addressBook[j].addr) and ngName ~= newGates[i].addr and ngName ~= "NEW GATE" and ngName ~= "NO GATES" and ngName ~= "Name" then
- addressBook[j].name = ngName
- gateChange = true
- end
- abNote, ngNote = addressBook[j].note, newGates[i].note
- if (abNote == "short note" or abNote == "Discovered gate" or abNote == "Added from logs") and ngNote ~= "short note" and ngNote ~= "Discovered gate" and ngNote ~= "Added from logs" then
- addressBook[j].note = ngNote
- gateChange = true
- end
- if addressBook[j].rating == "U" and newGates[i].rating ~= "U" then
- addressBook[j].rating = newGates[i].rating
- gateChange = true
- end
- abDim, ngDim = addressBook[j].loc.dim, newGates[i].loc.dim
- if (abDim == "Overworld" and ngDim ~= "Overworld" and ngDim ~= "Unspecified") or (abDim == "Unspecified" and ngDim ~= "Unspecified") then
- addressBook[j].loc.dim = ngDim
- gateChange = true
- end
- if addressBook[j].loc.x == 0 and addressBook[j].loc.y == 0 and addressBook[j].loc.z == 0 and (newGates[i].loc.x ~= 0 or newGates[i].loc.y ~= 0 or newGates[i].loc.z ~= 0) then
- addressBook[j].loc.x, addressBook[j].loc.y, addressBook[j].loc.z = newGates[i].loc.x, newGates[i].loc.y, newGates[i].loc.z
- gateChange = true
- end
- break --# stop the address book search loop and move on to the next gate in the 'new' list
- end
- end
- if matchFound then --# if a match was found...
- matchFound = false --# ...reset the variable for the next iteration of the loop
- else --# otherwise...
- addressBook[abCount + 1], abCount = { }, abCount + 1 --# initialize a new address book entry
- for k, v in pairs(newGates[i]) do --# loop through the gate entries...
- addressBook[abCount][k] = v --# ...and add them to the new address book entry
- end
- addressBook[abCount].iris = "none"
- addressBook[abCount].callDrop = false
- gateChange = true --# indicate that address book data has changed
- end
- end
- if abCount > 1 and (addressBook[1].name == "NO GATES" or addressBook[1].name == "Name") and addressBook[1].addr == "ADDRESS" then table.remove(addressBook, 1) abCount = abCount - 1 end
- gatePages = math.ceil(abCount / 12) --# repaginate AddressBook
- end
- local function saveData()
- local gateData = fs.open("/data/DHDgates", "w")
- gateData.write(textutils.serialize(addressBook))
- gateData.close()
- gateChange = false
- end
- local function repositionCursor()
- term.setTextColor(txtCol)
- term.setBackgroundColor(bgCol)
- term.setCursorPos(curX, curY)
- term.write(word)
- end
- local function netSend(dataPack)
- if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
- local encKey = tostring(host.id) .. "ccDHD!General_Comms*Key" .. thisCC
- local encryptedDataPack = encode(encrypt(encKey, textutils.serialize(dataPack)))
- rednet.send(host.id, encryptedDataPack, "ccDialerWiFi")
- end
- local function netReceive()
- local id, message, encKey, encryptedMessage, decryptedMessage, encodedMessage, success, iColor, mData
- while true do
- if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
- id, encodedMessage = rednet.receive("ccDialerWiFi")
- if id == host.id and type(encodedMessage) == "string" then
- success, encryptedMessage = pcall(decode, encodedMessage)
- if success then
- encKey = thisCC .. "ccDHD!General_Comms*Key" .. tostring(id)
- success, decryptedMessage = pcall(decrypt, encKey, encryptedMessage)
- if success then
- success, message = pcall(textutils.unserialize, decryptedMessage)
- if success and type(message) == "table" and message.program and message.program == "ccDialer" and message.data then
- mData = message.data
- if type(mData) == "string" then
- if mData == "pong" then
- missedPings = 0
- elseif mData == "ping" then
- netSend({ program = "ccDialer", gate = host.addr, command = "pong" })
- elseif mData == "open" or mData == "closed" or mData == "unk" then
- remoteIrisState = mData
- if runState ~= "Help" and runState ~= "viewing" and runState ~= "selectHost" then
- iColor = remoteIrisState == "open" and green or red
- iColor = remoteIrisState == "unk" and gray or iColor
- drawElement(6, 1, 1, 1, iColor, blue, "I") --# 'I' for Iris control
- if gettingInput then repositionCursor() end
- end
- elseif mData == "Idle" or mData == "Dialing" or mData == "Paused" or mData == "Connected" or mData == "Offline" then
- if mData == "Offline" or mData == "Idle" then
- remoteIrisState, dialAddress = "unk", "none"
- if mData == "Offline" then
- host = { id = nil, name = "NO HOST", addr = "NO HOST", ccDHD = false, status = nil }
- else
- host.status = mData
- end
- end
- if runState == "Dial" or runState == "goPage" or runState == "Sync" then
- if mData == "Offline" or mData == "Idle" then
- drawElement(6, 1, 1, 1, gray, blue, "I") --# 'I' for Iris control
- if mData == "Offline" and runState == "Sync" then
- runState = "Dial"
- drawElement(1, 5, termX - 1, termY - 5, nil, black) --# clear address book area
- end
- mData = mData == "Idle" and "Idle " or " "
- elseif mData == "Dialing" then
- mData = "Dialing "
- end
- if runState == "Dial" or runState == "goPage" then
- if mData == "Dialing " then
- drawElement(22, 2, 1, 1, silver, gray, mData) --# gate status
- else
- drawCLI() --# redraw the address book, et. al.
- end
- end
- if gettingInput then repositionCursor() end
- end
- end
- elseif type(mData) == "table" and message.command and message.command == "push" then
- mergeAddressBooks(mData)
- if gateChange then
- saveData()
- if host.addr == host.name and host.addr ~= "NO HOST" then
- for i = 1, #mData do
- if host.addr == mData[i].addr then
- host.name = mData[i].name
- break
- end
- end
- end
- if runState == "Dial" or runState == "goPage" then drawCLI() end
- if gettingInput then repositionCursor() end
- end
- end
- end
- end
- end
- end
- end
- end
- local function assignColor(gateNumber)
- return classifications[addressBook[gateNumber].rating].color or silver
- end
- local function assignRating(gateNumber)
- return classifications[addressBook[gateNumber].rating].long or (classifications[addressBook[gateNumber].rating].label or "Unknown/Unclassified")
- end
- local function clearScreen(bgColor)
- term.setBackgroundColor(bgColor or black)
- term.clear()
- end
- local function shutDown()
- if host.id then netSend({ program = "ccDialer", gate = host.addr, command = "logout" }) end
- if rednet.isOpen(modemSide) then rednet.close(modemSide) end
- clearScreen()
- term.setTextColor(white)
- term.setCursorPos(1, 1)
- end
- do
- local validColors = { [1] = true; [2] = true; [4] = true; [8] = true; [16] = true; [32] = true; [64] = true; [128] = true; [256] = true; [512] = true; [1024] = true; [2048] = true; [4096] = true; [8192] = true; [16384] = true, [32768] = true; }
- drawElement = function(x, y, w, h, txtColor, bgColor, text)
- if type(x) == "number" and type(y) == "number" and type(w) == "number" and type(h) == "number" then
- local sText = text and tostring(text) or "" --# Ensure text is a string
- w = math.floor(math.min(math.max(1, math.max(#sText, w)), termX)) --# validate width
- h = math.floor(math.max(1, math.min(h, termY))) --# validate height
- x = math.floor(math.max(1, math.min(x, termX - w + 1))) --# validate x coord
- y = math.floor(math.max(1, math.min(y, termY - h + 1))) --# validate y coord
- local spacer = (w - #sText) / 2
- local txtLine = string.rep(" ", math.floor(spacer)) .. sText .. string.rep(" ", math.ceil(spacer))
- if type(txtColor) == "number" and validColors[txtColor] then term.setTextColor(txtColor) end --# validate the text color
- if type(bgColor) == "number" and validColors[bgColor] then term.setBackgroundColor(bgColor) end --# validate the background color
- if h == 1 then --# if the height is 1 then...
- term.setCursorPos(x, y) --# Position cursor
- term.write(txtLine) --# Draw the single line of the 'element'
- else --# otherwise...
- local line, txtRow = string.rep(" ", w), math.floor(h / 2) + y --# Define one line of the 'element' (box/rectangle/line-seg) and the row the text will be drawn on
- for i = y, y + h - 1 do --# Loop through the height of the 'element' line by line (top to bottom)
- term.setCursorPos(x, i) --# Position cursor
- term.write(i == txtRow and txtLine or line) --# Draw 1 of h lines of the 'element' (box/rectangle)
- end
- end
- end
- end
- end
- local function drawRatingList(rating)
- local txtColor
- drawElement(7, 4, 19, 8, nil, gray) --# menu body
- for k, v in pairs(classifications) do
- if rating == k then
- txtColor = rating == "U" and white or v.color
- drawElement(25, v.order + 3, 1, 1, nil, v.color) --# selected rating color pip
- else
- txtColor = silver
- end
- drawElement(6, v.order + 3, 1, 1, nil, v.color) --# color pip
- drawElement(8, v.order + 3, 1, 1, txtColor, gray, v.label)
- end
- end
- local function drawHeader()
- local spacer, title = termX / 2, ""
- if runState == "viewing" then
- local titleText = addressBook[currentEdit].name
- spacer = (termX - #titleText) / 2
- title = string.rep(" ", math.floor(spacer)) .. titleText .. string.rep(" ", math.ceil(spacer) - 3) .. "[ ]"
- elseif runState == "Help" then
- title = string.rep(" ", math.floor(spacer) - 4) .. "ccDialer" .. string.rep(" ", math.ceil(spacer) - 7) .. "[ ]"
- else
- title = "[ ] [ ]" .. string.rep(" ", math.floor(spacer) - 11) .. "ccDialer" .. string.rep(" ", math.ceil(spacer) - 7) .. "[ ]"
- end
- drawElement(1, 1, termX, 1, white, runState == "viewing" and assignColor(currentEdit) or blue, title) --# title bar
- if runState ~= "viewing" and runState ~= "Help" then
- drawElement(2, 1, 1, 1, host.id and green or gray, nil, "H") --# 'H' for Sync Hosts
- local iColor = remoteIrisState == "open" and green or red
- iColor = remoteIrisState == "unk" and gray or iColor
- drawElement(6, 1, 1, 1, iColor, nil, "I") --# 'I' for Iris control
- end
- local xColor = red
- if runState == "viewing" and addressBook[currentEdit].rating == "D" then xColor = white end
- drawElement(termX - 1, 1, 1, 1, xColor, nil, "X") --# 'X' for Exit
- if runState == "Dial" then
- drawElement(1, 2, termX, 1, nil, gray)
- drawElement(20, 2, 1, 1, nil, blue, " ")
- drawElement(2, 2, 1, 1, silver, gray, host.name)
- drawElement(22, 2, 1, 1, nil, nil, host.status or " ")
- drawElement(1, 3, (termX / 2) - 1, 1, host.ccDHD and white or gray, host.ccDHD and cyan or silver, "Sync")
- term.setBackgroundColor(green)
- term.setTextColor(gateChange and orange or white)
- term.write(" + ")
- term.setBackgroundColor(red)
- if gateChange then term.setTextColor(white) end --# change the text color back to white if it was changed to orange
- term.write(" END Call ")
- elseif runState == "Sync" then
- drawElement(1, 2, termX, 1, nil, gray)
- drawElement(20, 2, 1, 1, nil, blue, " ")
- drawElement(2, 2, 1, 1, silver, gray, host.name)
- drawElement(22, 2, 1, 1, nil, nil, host.status or " ")
- elseif runState == "Help" then
- drawElement(1, 2, termX, 1, silver, gray, "+ button")
- elseif runState == "viewing" then
- drawElement(1, 2, termX, 1, silver, gray, "Gate Information")
- end
- end
- local function drawHostsUI()
- local yPos, hostCount, magicNumber = 1, #hosts, ((syncPage - 1) * 5) + 1
- drawElement(11, 1, 16, 1, white, blue, tostring(hostCount) .. " Hosts") --# Header
- drawElement(11, 2, 16, 11, nil, gray) --# Body
- for i = magicNumber, math.min(hostCount, magicNumber + 4) do
- yPos = yPos + 2
- drawElement(12, yPos, 14, 1, nil, hosts[i].ccDHD and green or red) --# Indicate ccDHD host on left & right side of hostName
- drawElement(13, yPos, 12, 1, nil, silver, hosts[i].name) --# hostName (host name or address)
- end
- drawElement(11, 13, 16, 1, silver, blue, "Page " .. tostring(syncPage) .. " of " .. tostring(syncPages)) --# footer
- end
- local function drawPasswordUI()
- drawElement(2, 5, termX - 2, 1, gray, black, "Remote password") --# header
- drawElement(12, 6, 16, 3, nil, silver) --# body
- drawElement(13, 7, 14, 1, nil, gray) --# input area
- end
- local function drawGoPagePopUp()
- drawElement((termX / 2) - 3, termY - 3, 1, 1, white, gray, " :Page: ") --# header
- drawElement((termX / 2) - 3, termY - 2, 8, 2) --# body
- drawElement((termX / 2) - 2, termY - 2, 6, 1, nil, black) --# input area bg
- end
- local function drawNaviUI()
- pNum = tostring(gatePage) .. " of " .. tostring(gatePages)
- drawElement((termX / 2) - 10, termY, 1, 1, gray, black, gatePages > 1 and "<< < > >>" or " ") --# fwd/back buttons
- drawElement(((termX - #pNum) / 2) + 1, termY, 1, 1, silver, nil, pNum) --# page number
- end
- local function drawHelpScreen()
- drawElement(1, 5, termX, 1, silver, gray, "Address Book")
- drawElement(1, 9, termX, 1, nil, nil, "Header buttons")
- --drawElement(1, 13, termX, 1, nil, nil, "Sync button")
- if termY > 12 then drawElement(1, termY, termX, 1, nil, nil, "version " .. ccDialVer) end
- drawElement(2, 3, 1, 1, black, white, "(L) click: Add address")
- drawElement(2, 4, 1, 1, nil, nil, "(R) click: Save addr book")
- drawElement(2, 6, 1, 1, nil, nil, "(L) click: dial address")
- drawElement(2, 7, 1, 1, nil, nil, "(R) click: view or edit")
- drawElement(2, 8, 1, 1, nil, nil, "(M) click: del. address")
- drawElement(2, 10, 1, 1, nil, nil, "[H] = locate Sync host")
- drawElement(2, 11, 1, 1, nil, nil, "[I] = open remote iris")
- drawElement(2, 12, 1, 1, nil, nil, "[X] = Exit/Quit")
- --drawElement(2, 14, 1, 1, nil, nil, "(L) click: Send/Receive addr book")
- end
- local function drawSyncScreen()
- drawElement(2, 4, termX - 2, 3, white, green, "GET Address Book")
- drawElement(2, 7, termX - 2, 3, nil, orange, "SEND Address Book")
- drawElement(2, 10, termX - 2, 3, nil, red, "C A N C E L")
- end
- local function drawGateData()
- local addr, note, breakPoint = addressBook[currentEdit].addr, addressBook[currentEdit].note, 37
- drawElement(2, 4, 1, 1, cyan, black, addressBook[currentEdit].name .. string.rep(" ", 12 - #addressBook[currentEdit].name)) --# Name
- if host.addr == addr or host.addr:sub(1, 7) == addr or host.addr == addr:sub(1, 7) then
- drawElement(15, 6, 1, 1, gray, nil, "Sync Host") --# Sync Host
- elseif addr == dialAddress or addr:sub(1, 7) == dialAddress or addr == dialAddress:sub(1, 7) then
- drawElement(15, 6, 1, 1, gray, nil, "Target ") --# Dial target
- else
- drawElement(15, 6, 9, 1)
- end
- drawElement(2, 6, 1, 1, yellow, nil, addr .. " ") --# Address
- local ratingWord = assignRating(currentEdit)
- drawElement(2, 8, 1, 1, silver, nil, "[" .. ratingWord .. "]" .. string.rep(" ", termX - #ratingWord - 3))
- drawElement(3, 8, 1, 1, assignColor(currentEdit), nil, ratingWord) --# Classification
- drawElement(20, 4, 1, 1, white, nil, tostring(currentEdit) .. string.rep(" ", 5 - #tostring(currentEdit))) --# Address book entry number ('#')
- if #note > 37 then --# Note
- breakPoint = note:sub(32, 38):find(" ") + 30
- else
- breakPoint = #note
- end
- drawElement(2, 10, 1, 1, nil, nil, note:sub(1, breakPoint) .. string.rep(" ", 37 - breakPoint))
- if #note > 37 then
- drawElement(2, 11, 1, 1, nil, nil, note:sub(breakPoint + 2) .. string.rep(" ", 37 - #note:sub(breakPoint + 2)))
- else
- drawElement(2, 11, 37, 1) --# clear line
- end
- local xStr, yStr, zStr = tostring(addressBook[currentEdit].loc.x), tostring(addressBook[currentEdit].loc.y), tostring(addressBook[currentEdit].loc.z)
- drawElement(26, 4, 1, 1, brown, nil, addressBook[currentEdit].loc.dim:sub(1, 13) .. string.rep(" ", 13 - #addressBook[currentEdit].loc.dim:sub(1, 13))) --# Dimension
- drawElement(29, 5, 1, 1, silver, nil, xStr .. string.rep(" ", 9 - #xStr)) --# X
- drawElement(29, 6, 1, 1, nil, nil, yStr .. string.rep(" ", 9 - #yStr)) --# Y
- drawElement(29, 7, 1, 1, nil, nil, zStr .. string.rep(" ", 9 - #zStr)) --# Z
- drawElement((termX / 2) + 2, termY, 7, 1, red, gray, "Close")
- drawElement((termX / 2) - 8, termY, 6, 1, gateChange and green or silver, nil, "Save")
- end
- local function drawGateLabels(id)
- runState, currentEdit = "viewing", id
- clearScreen()
- drawHeader()
- drawElement(18, 4, 1, 1, gray, black, "#")
- --drawElement(2, 13, 4, 1, nil, nil, "Dim:")
- drawElement(26, 5, 2, 1, nil, nil, "x:")
- drawElement(26, 6, 2, 1, nil, nil, "y:")
- drawElement(26, 7, 2, 1, nil, nil, "z:")
- drawGateData()
- end
- drawAddressBook = function() --# Gate Address Book
- local xPos, yPos, magicNumber, hostAddr = 1, 5, ((gatePage - 1) * 12) + 1, host.addr
- local txtColor, bgColor, addr
- for i = magicNumber, math.min(abCount, magicNumber + 11) do
- addr = addressBook[i].addr
- txtColor = hostAddr == addr and silver or (dialAddress == addr and black or white)
- bgColor = hostAddr == addr and gray or assignColor(i)
- drawElement(xPos, yPos, 12, 1, txtColor, bgColor, addressBook[i].name)
- yPos = yPos + 2
- if yPos > 11 then xPos = xPos + 13 yPos = 5 end
- end
- end
- do
- local stateTable = {
- Dial = function() drawAddressBook() drawNaviUI() end;
- Sync = function() drawSyncScreen() end;
- Help = function() drawHelpScreen() end;
- goPage = function()
- drawAddressBook()
- drawNaviUI()
- drawGoPagePopUp()
- end;
- }
- drawCLI = function()
- if runState ~= "selectHost" then drawHeader() end
- stateTable[runState]()
- end
- end
- local function flashDial(gateName, x, y, gateNum)
- local gColor = assignColor(gateNum)
- drawElement(x, y, 12, 1, gColor, black, gateName)
- sleep(0.1)
- drawElement(x, y, 12, 1, dialAddress == addressBook[gateNum].addr and black or white, gColor, gateName)
- end
- local function flashChoice(x, y, w, h, txtClr, bgClr, label)
- drawElement(x, y, w, h, txtClr, bgClr, label)
- sleep(0.1)
- end
- local function gateDataChange()
- gateChange = true
- drawElement((termX / 2) - 8, termY, 6, 1, green, gray, "Save")
- end
- local function addNewAddress(fast)
- if abCount == 1 and addressBook[1].name == "NO GATES" and addressBook[1].addr == "ADDRESS" then
- addressBook[1].name = "Name"
- else
- abCount = abCount + 1
- addressBook[abCount] = { name = "Name", addr = "ADDRESS", rating = "U", iris = "none", callDrop = false, note = "short note", loc = { x = 0, y = 0, z = 0, dim = "Unspecified", }, }
- end
- gateChange = true
- gatePages = math.ceil(abCount / 12) --# Re-paginate AddressBook
- if fast then --# if the new gate is a quick-add....
- if gatePage == gatePages then drawAddressBook() end --# ...and we're on the last page, redraw the address list
- drawNaviUI()
- else
- drawGateLabels(abCount)
- end
- end
- local function selectHost() --# host selection during initialization
- local hostCount, event, data, x, y, magicNumber, yPos = #hosts
- while hosts[1] do
- event, data, x, y = os.pullEvent()
- if event == "mouse_click" and x > 11 and x < 26 and y > 2 and y < 12 and data == 1 then
- yPos, magicNumber = 1, ((syncPage - 1) * 5) + 1
- for i = magicNumber, math.min(hostCount, magicNumber + 4) do
- yPos = yPos + 2
- if y == yPos then
- flashChoice(12, yPos, 14, 1, black, white, hosts[i].name)
- host.id, host.addr, host.ccDHD, host.name, host.status, remoteIrisState = hosts[i].id, hosts[i].addr, hosts[i].ccDHD, hosts[i].name, hosts[i].gStatus, hosts[i].remoteIris
- pingTimer = os.startTimer(5)
- return
- end
- end
- elseif event == "mouse_scroll" and ((data == 1 and syncPage < syncPages) or (data == -1 and syncPage > 1)) then
- syncPage = syncPage + data
- drawHostsUI()
- elseif event == "key" and syncPages > 1 then
- if (data == keys.pageUp and syncPage > 1) or (data == keys.pageDown and syncPage < syncPages) then
- syncPage = data == keys.pageUp and syncPage - 1 or syncPage + 1
- drawHostsUI()
- elseif (data == keys.home and syncPage > 1) or (data == keys["end"] and syncPage < syncPages) then
- syncPage = data == keys.home and 1 or syncPages
- drawHostsUI()
- end
- end
- end
- end
- local function findHosts()
- if runState ~= "init" then
- drawElement((termX / 2) - 9, 5, 20, 1, white, blue, "Sync") --# Sync search header
- drawElement((termX / 2) - 9, 6, 20, 3, nil, gray, "Locating hosts ...") --# Sync search body
- end
- for i = #hosts, 1, -1 do hosts[i] = nil end --# clear hosts table
- host.id, host.name, host.addr, host.status, remoteIrisState, syncPage, syncPages = nil, "NO HOST", "NO HOST", nil, "unk", 1, 1 --# reset host and remoteIrisState
- local hostIDs = { rednet.lookup("ccDialerWiFi") } --# populate hostIDs table
- local hostCount = #hostIDs
- if hostCount > 0 then
- local id, message, encryptedMessage, decryptedMessage, encodedMessage, encKey, success, msgData, msgLen, gateMatch, hostAddr, tempState, _, button, x, y
- for i = 1, hostCount do --# process host list
- hosts[i] = { }
- hosts[i].id, host.id, host.addr, host.ccDHD, host.status = hostIDs[i], hostIDs[i], "NO HOST", false, nil --# set the host and reset the gate information
- netSend({ program = "ccDialer", gate = "NO HOST", command = "QRY" }) --# query the host
- id, encodedMessage = rednet.receive("ccDialerWiFi", 1)
- if id == host.id and type(encodedMessage) == "string" then
- success, encryptedMessage = pcall(decode, encodedMessage)
- if success then
- encKey = thisCC .. "ccDHD!General_Comms*Key" .. tostring(id)
- success, decryptedMessage = pcall(decrypt, encKey, encryptedMessage)
- if success then
- success, message = pcall(textutils.unserialize, decryptedMessage)
- if success and type(message) == "table" and message.program and message.program == "ccDialer" and message.data and type(message.data) == "string" and type(message.ccDHD) == "boolean" then
- msgData = message.data
- msgLen = #msgData
- if msgLen == 7 or msgLen == 9 or msgData == "lockdown" then
- host.addr, host.ccDHD, host.status, remoteIrisState = msgData, message.ccDHD, message.gStatus, message.iris
- if host.addr ~= "lockdown" then --and host.addr ~= "NO HOST" then
- gateMatch, hostAddr, hosts[i].name = false, host.addr, host.addr
- for j = 1, abCount do --# Cycle through addressBook (for an entry matching host's address)
- if hostAddr == addressBook[j].addr then --# if a match is found...
- hosts[i].name = addressBook[j].name --# ...set the hostName to the name of the addressBook entry...
- gateMatch = true --# ...indicate a match has been found and...
- break --# ...stop the addressBook loop and continue with the next hostAddress
- end
- end
- if not gateMatch then --# Found a new gate
- clearScreen()
- if runState ~= "init" then drawHeader() end
- tempState = runState
- runState = "newGate"
- drawElement(11, 4, 19, 1, white, blue, "New Gate Detected") --# New gate header
- drawElement(11, 5, 19, 5, nil, gray) --# New gate body
- drawElement(#hostAddr == 9 and 16 or 17, 6, 1, 1, silver, nil, hostAddr)
- drawElement(14, 8, 5, 1, white, green, "Add")
- drawElement(21, 8, 6, 1, nil, orange, "Skip")
- while true do
- _, button, x, y = os.pullEvent("mouse_click")
- if x > 13 and x < 19 and y == 8 and button == 1 then --# Add Gate
- if abCount == 1 and (addressBook[1].name == "NO GATES" or addressBook[1].name == "Name") and addressBook[1].addr == "ADDRESS" then
- addressBook[1].name, addressBook[1].addr, addressBook[1].note = hostAddr, hostAddr, "Discovered gate"
- else
- abCount = abCount + 1
- addressBook[abCount] = { name = hostAddr, addr = hostAddr, rating = "U", iris = "none", callDrop = false, note = "Discovered gate", loc = { x = 0, y = 0, z = 0, dim = "Unspecified", }, }
- gatePages = math.ceil(abCount / 12) --# Re-paginate AddressBook
- end
- saveData()
- break
- elseif x > 20 and x < 27 and y == 8 and button == 1 then --# Skip
- break
- end
- end
- runState = tempState
- drawElement(11, 4, 19, 6, nil, black) --# Clear pop-up
- end
- end
- end
- end
- end
- end
- end
- hosts[i].addr, hosts[i].ccDHD, hosts[i].gStatus, hosts[i].remoteIris = host.addr, host.ccDHD, host.status, remoteIrisState --# store gate address and ccDHD status
- end
- host.id, host.addr, host.status, remoteIrisState = nil, "NO HOST", nil, "unk" --# clear host info
- for i = hostCount, 1, -1 do --# process and sanitize host list
- if hosts[i].addr == "lockdown" or hosts[i].addr == "NO HOST" then --# if gate is in lockdown or hasn't replied...
- table.remove(hosts, i) --# ...remove entry from hosts table
- end
- end
- hostCount = #hosts
- syncPages = math.ceil(hostCount / 5)
- end
- if hostCount > 1 then --# more than 1 host responds
- if runState == "init" then
- drawElement(2, 2, termX - 1, termY - 1, nil, black) --# clear most of screen
- drawHostsUI()
- selectHost()
- else
- drawElement(1, 1, termX, termY, nil, black) --# clear screen
- drawHostsUI()
- end
- elseif hostCount == 1 then --# only one host responds
- host.id, host.name, host.addr, host.ccDHD, host.status, remoteIrisState = hosts[1].id, hosts[1].name, hosts[1].addr, hosts[1].ccDHD, hosts[1].gStatus, hosts[1].remoteIris --# set host ID, gate address, and ccDHD status
- if runState == "init" then
- drawElement(5, 12, 1, 1, green, gray, "O")
- else
- runState = "Dial"
- drawElement(10, 5, 20, 4, nil, black) --# clear Sync pop-up
- drawCLI()
- end
- else --# no hosts respond
- host.ccDHD, host.addr, host.name, host.id, host.status, remoteIrisState = false, "NO HOST", "NO HOST", nil, nil, "unk"
- if runState == "init" then
- drawElement(5, 12, 1, 1, red, gray, "0")
- else
- runState = "Dial"
- drawElement(10, 5, 20, 4, nil, black) --# clear Sync pop-up
- drawCLI()
- end
- end
- end
- --# custom read function courtesy of theoriginalbit (modified by Dog)
- local function read(_mask, _history, _limit, _noTerminate, _noMouse, _gField)
- if _mask and type(_mask) ~= "string" then
- error("Invalid parameter #1: Expected string, got " .. type(_mask), 2)
- end
- if _history and type(_history) ~= "table" then
- error("Invalid parameter #2: Expected table, got " .. type(_history), 2)
- end
- if _limit and type(_limit) ~= "number" then
- error("Invalid parameter #3: Expected number, got " .. type(_limit), 2)
- end
- if _noTerminate and type(_noTerminate) ~= "boolean" then
- error("Invalid argument #4: Expected boolean, got " .. nativeType(_noTerminate), 2)
- end
- if _noMouse and type(_noMouse) ~= "boolean" then
- error("Invalid argument #5: Expected boolean, got " .. nativeType(_noMouse), 2)
- end
- if _gField and type(_gField) ~= "boolean" then
- error("Invalid argument #6: Expected boolean, got " .. nativeType(_gField), 2)
- end
- term.setCursorBlink(true)
- gettingInput = true
- 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 }
- local mouseLimit = _limit or 0
- local input, lineCleared = "", false
- word = ""
- local pos = 0
- local historyPos = nil
- local pullEvent = _noTerminate and os.pullEventRaw or os.pullEvent
- local sw, sh = term.getSize()
- local sx, sy = term.getCursorPos()
- curX, curY = sx, sy
- local function redraw(_special)
- local scroll = (sx + pos >= sw and (sx + pos) - sw or 0)
- local replace = _special or _mask
- local output = replace and (string.rep(replace, math.ceil(#input / #replace) - scroll)):sub(1, #input) or input:sub(scroll + 1)
- term.setCursorPos(sx, sy)
- term.write(output)
- term.setCursorPos(sx + pos - scroll, sy)
- end
- local nativeScroll = term.scroll
- term.scroll = function(_n) local ok, err = pcall(function() return nativeScroll(_n) end) if ok then sy = sy - _n return err end error(err, 2) end
- local function historyHandler(value)
- redraw(' ')
- if value == -1 or value == keys.up then
- if not historyPos then
- historyPos = #_history
- elseif historyPos > 1 then
- historyPos = historyPos - 1
- end
- else
- if historyPos ~= nil and historyPos < #_history then
- historyPos = historyPos + 1
- elseif historyPos == #_history then
- historyPos = nil
- end
- end
- if historyPos and #_history > 0 then
- input = string.sub(_history[historyPos], 1, _limit) or ""
- pos = #input
- word = input
- if not _limit then mouseLimit = pos end
- else
- input = ""
- pos = 0
- word = input
- if not _limit then mouseLimit = 0 end
- end
- end
- while true do
- local event, code, x, y = pullEvent()
- if event == "char" and (not _limit or #input < _limit) then
- if not lineCleared and pos == 0 then term.write(string.rep(" ", _limit)) lineCleared = true end
- local goodData = false
- if _gField then
- if symbols[code:upper()] then
- code = code:upper()
- goodData = true
- end
- else
- goodData = true
- end
- if goodData then
- input = input:sub(1, pos) .. code .. input:sub(pos + 1)
- pos = pos + 1
- word = input
- if not _limit then mouseLimit = math.min(mouseLimit + 1, sw - (sw - sx)) end
- end
- elseif event == "paste" and (not _limit or #input < _limit) then
- if not lineCleared and pos == 0 then term.write(string.rep(" ", _limit)) lineCleared = true end
- if _limit and #input + #code > _limit then
- code = code:sub(1, _limit - #input)
- end
- if _gField then
- local newWord, glyph = { }, ""
- for i = 1, #code do
- glyph = string.upper(code:sub(i, i))
- newWord[i] = symbols[glyph] and glyph or "?"
- end
- code = table.concat(newWord)
- end
- input = input:sub(1, pos) .. code .. input:sub(pos + 1)
- pos = pos + #code
- word = input
- if not _limit then mouseLimit = math.min(mouseLimit + #code, sw - (sw - sx)) end
- elseif event == "key" then
- if code == keys.enter or code == keys.numPadEnter then
- break
- elseif code == keys.backspace and pos > 0 then
- redraw(' ')
- input = input:sub(1, math.max(pos - 1, 0)) .. input:sub(pos + 1)
- pos = math.max(pos - 1, 0)
- word = input
- if not _limit then mouseLimit = math.max(mouseLimit - 1, 0) end
- elseif code == keys.delete and pos < #input then
- redraw(' ')
- input = input:sub(1, pos)..input:sub(pos + 2)
- word = input
- if not _limit then mouseLimit = math.max(mouseLimit - 1, 0) end
- elseif code == keys.home then
- pos = 0
- elseif code == keys["end"] then
- pos = #input
- elseif code == keys.left and pos > 0 then
- pos = math.max(pos - 1, 0)
- elseif code == keys.right and pos < #input then
- pos = math.min(pos + 1, #input)
- elseif _history and code == keys.up or code == keys.down then
- historyHandler(code)
- end
- elseif event == "mouse_click" and not _noMouse and ((x < sx or x >= sx + mouseLimit) or (y ~= sy)) then
- break
- elseif event == "mouse_scroll" and _history then
- historyHandler(code)
- end
- redraw(_mask)
- end
- term.scroll = nativeScroll
- term.setCursorBlink(false)
- if sy + 1 > sh then
- term.scroll(sy + 1 - sh)
- term.setCursorPos(1, sy)
- else
- term.setCursorPos(1, sy + 1)
- end
- word = ""
- gettingInput = false
- return input
- end
- local function remotePassword()
- drawElement(1, 4, termX, termY - 3, nil, black) --# clear most of screen
- drawPasswordUI()
- runState, bgCol, txtCol = "RemotePass", gray, white
- term.setBackgroundColor(gray)
- term.setTextColor(white)
- term.setCursorPos(14, 7)
- local remotePass = read("*", nil, 12)
- if remotePass ~= "" then
- local pass = table.concat(pbkdf2(remotePass, "ccDHD!Pass.Hash", 15))
- netSend({ program = "ccDialer", gate = host.addr, password = pass })
- end
- drawElement(2, 5, termX - 2, 5, nil, black) --# clear iris pop-up
- runState = "Dial"
- drawAddressBook()
- drawNaviUI()
- end
- local function saveAndQuit()
- local tX, tY = math.floor(termX / 2), math.floor(termY / 2)
- drawElement(tX - 9, tY - 1, 1, 1, white, blue, " Save Addr Book ? ")
- drawElement(tX - 9, tY, 20, 3, nil, gray)
- drawElement(tX - 8, tY + 1, 1, 1, nil, green, " Save ")
- drawElement(tX + 2, tY + 1, 1, 1, nil, orange, " Quit ")
- while true do
- local _, button, x, y = os.pullEvent("mouse_click")
- if x > tX - 10 and x < tX + 11 and y > tY - 2 and y < tY + 3 then
- if x > tX - 9 and x < tX and y == tY + 1 and button == 1 then
- flashChoice(tX - 8, tY + 1, 8, 1, green, white, "Save")
- saveData()
- return true
- elseif x > tX + 1 and x < tX + 10 and y == tY + 1 and button == 1 then
- flashChoice(tX + 2, tY + 1, 8, 1, orange, white, "Quit")
- return true
- end
- else
- drawElement(tX - 9, tY - 1, 20, 4, nil, black) --# clear save & quit pop-up
- drawAddressBook()
- return false
- end
- end
- end
- local function userInput()
- local event, data, x, y
- while true do
- event, data, x, y = os.pullEvent()
- if event == "mouse_click" then
- if y == 1 and runState ~= "selectHost" then
- if x > termX - 3 and data == 1 then --# 'X' (exit button)
- flashChoice(termX - 1, 1, 1, 1, white, runState == "viewing" and assignColor(currentEdit) or blue, "X")
- drawElement(termX - 1, 1, 1, 1, red, nil, "X")
- if runState == "Dial" then
- if gateChange then
- if saveAndQuit() then shutDown() break end
- else
- shutDown()
- break
- end
- else
- runState = "Dial"
- clearScreen()
- drawCLI()
- end
- elseif x < 4 and runState == "Dial" and data == 1 then --# 'H' (hosts button)
- drawElement(2, 1, 1, 1, gray, blue, "H")
- drawElement(6, 1, 1, 1, nil, nil, "I")
- drawElement(2, 2, 1, 1, silver, gray, "NO HOST ")
- drawElement(22, 2, 10, 1)
- drawElement(1, 3, termX, termY - 2, nil, black) --# clear most of screen
- if host.id then netSend({ program = "ccDialer", gate = host.addr, command = "logout" }) end
- runState, remoteIrisState = "selectHost", "unk"
- findHosts()
- elseif x > 4 and x < 8 and runState == "Dial" then --# 'I' (iris button)
- if data == 1 and host.status == "Connected" then --# host.addr ~= "NO HOST"
- remotePassword()
- elseif data == 3 then
- remoteIrisState = "unk"
- drawElement(6, 1, 1, 1, gray, blue, "I") --# 'I' for Iris control
- end
- end
- end
- if runState == "viewing" then --# Gate Viewing / Editing
- if y == 4 and data == 1 then
- if x > 1 and x < 11 then --# Name
- drawElement(2, 4, 1, 1, gray, black, addressBook[currentEdit].name)
- bgCol, txtCol = black, cyan
- term.setTextColor(cyan)
- term.setCursorPos(2, 4)
- local newGateName = read(nil, { addressBook[currentEdit].addr, addressBook[currentEdit].name }, 12)
- if newGateName ~= addressBook[currentEdit].name and newGateName ~= "" then
- addressBook[currentEdit].name = newGateName
- gateDataChange()
- end
- drawElement(2, 4, 1, 1, cyan, black, addressBook[currentEdit].name .. string.rep(" ", 12 - #addressBook[currentEdit].name))
- elseif x > 19 and x < 20 + #tostring(currentEdit) then --# Position / reorder
- drawElement(20, 4, 1, 1, gray, black, currentEdit)
- bgCol, txtCol = black, white
- term.setTextColor(white)
- term.setCursorPos(20, 4)
- local newPos = tonumber(read(nil, nil, 5))
- if newPos and newPos > 0 and newPos <= abCount and newPos ~= currentEdit then
- local tempGateData = { }
- for k, v in pairs(addressBook[currentEdit]) do
- tempGateData[k] = v
- end
- table.remove(addressBook, currentEdit)
- table.insert(addressBook, newPos, tempGateData)
- currentEdit = newPos
- gateDataChange()
- end
- drawElement(20, 4, 1, 1, white, black, tostring(currentEdit) .. string.rep(" ", 5 - #tostring(currentEdit)))
- elseif x > 25 and x < termX then --# Dimension
- drawElement(26, 4, 1, 1, gray, black, addressBook[currentEdit].loc.dim:sub(1, 13))
- bgCol, txtCol = black, brown
- term.setTextColor(brown)
- term.setCursorPos(26, 4)
- local newDim = read(nil, { "Overworld", "Nether", "The End", addressBook[currentEdit].loc.dim }, 19)
- if newDim ~= "" and newDim ~= addressBook[currentEdit].loc.dim then
- addressBook[currentEdit].loc.dim = newDim
- if #newDim > 13 then drawElement(26, 4, 13, 1) end
- gateDataChange()
- end
- drawElement(26, 4, 1, 1, brown, black, addressBook[currentEdit].loc.dim:sub(1, 13) .. string.rep(" ", 13 - #addressBook[currentEdit].loc.dim:sub(1, 13)))
- end
- elseif y == 5 and x > 28 and x < termX - 1 and data == 1 then --# X
- local xStr = tostring(addressBook[currentEdit].loc.x)
- drawElement(29, 5, 1, 1, gray, black, xStr)
- bgCol, txtCol = black, silver
- term.setTextColor(silver)
- term.setCursorPos(29, 5)
- local newX = tonumber(read(nil, { xStr }, 9))
- if newX and newX ~= addressBook[currentEdit].loc.x then
- addressBook[currentEdit].loc.x = newX
- gateDataChange()
- end
- drawElement(29, 5, 1, 1, silver, black, tostring(addressBook[currentEdit].loc.x) .. string.rep(" ", 9 - #tostring(addressBook[currentEdit].loc.x)))
- elseif y == 6 then
- if x > 1 and x < 11 then --# Address
- if data == 1 then
- drawElement(2, 6, 1, 1, gray, black, addressBook[currentEdit].addr)
- bgCol, txtCol = black, yellow
- term.setTextColor(yellow)
- term.setCursorPos(2, 6)
- local newGateAddress = read(nil, { addressBook[currentEdit].addr }, 9, nil, nil, true)
- local ngaLen, name = #newGateAddress, addressBook[currentEdit].name
- if (ngaLen == 7 or ngaLen == 9) and newGateAddress ~= addressBook[currentEdit].addr and not newGateAddress:find("?") then
- addressBook[currentEdit].addr = newGateAddress
- if name == "Name" or name == "NEW GATE" or name == "NO GATES" then
- addressBook[currentEdit].name = newGateAddress
- drawElement(2, 4, 1, 1, sky, nil, addressBook[currentEdit].name .. string.rep(" ", 12 - #addressBook[currentEdit].name))
- end
- gateDataChange()
- end
- drawElement(2, 6, 1, 1, yellow, black, addressBook[currentEdit].addr .. " ")
- elseif data == 2 and host.id and addressBook[currentEdit].addr ~= host.addr then
- netSend({ program = "ccDialer", gate = host.addr, command = addressBook[currentEdit].addr })
- runState = "Dial"
- clearScreen()
- drawCLI()
- end
- elseif x > 28 and x < termX - 1 and data == 1 then --# Y
- local yStr = tostring(addressBook[currentEdit].loc.y)
- drawElement(29, 6, 1, 1, gray, black, yStr)
- bgCol, txtCol = black, silver
- term.setTextColor(silver)
- term.setCursorPos(29, 6)
- local newY = tonumber(read(nil, { yStr }, 9))
- if newY and newY ~= addressBook[currentEdit].loc.y then
- addressBook[currentEdit].loc.y = newY
- gateDataChange()
- end
- drawElement(29, 6, 1, 1, silver, black, tostring(addressBook[currentEdit].loc.y) .. string.rep(" ", 9 - #tostring(addressBook[currentEdit].loc.y)))
- end
- elseif y == 7 and x > 28 and x < termX - 1 and data == 1 then --# Z
- local zStr = tostring(addressBook[currentEdit].loc.z)
- drawElement(29, 7, 1, 1, gray, black, zStr)
- bgCol, txtCol = black, silver
- term.setTextColor(silver)
- term.setCursorPos(29, 7)
- local newZ = tonumber(read(nil, { zStr }, 9))
- if newZ and newZ ~= addressBook[currentEdit].loc.z then
- addressBook[currentEdit].loc.z = newZ
- gateDataChange()
- end
- drawElement(29, 7, 1, 1, silver, black, tostring(addressBook[currentEdit].loc.z) .. string.rep(" ", 9 - #tostring(addressBook[currentEdit].loc.z)))
- elseif y == 8 and x > 1 and x < 4 + #assignRating(currentEdit) and data == 1 then --# Classification
- drawRatingList(addressBook[currentEdit].rating)
- local selected = false
- while true do
- local _, button, mX, mY = os.pullEvent("mouse_click")
- if mX > 5 and mX < 26 and mY > 3 and mY < 12 and button == 1 then
- for k, v in pairs(classifications) do
- if mY == v.order + 3 then
- if addressBook[currentEdit].rating ~= k then
- addressBook[currentEdit].rating = k
- gateDataChange()
- end
- selected = true
- break
- end
- end
- if selected then break end
- else
- break
- end
- end
- drawElement(6, 4, 20, 11, nil, black) --# clear menu body
- drawElement(19, 4, 1, 1, gray, black, "#")
- if gateChange and selected then drawHeader() end
- drawGateData()
- elseif ((y == 10 and x > 1 and x < 2 + #addressBook[currentEdit].note:sub(1, 37)) or (y == 11 and #addressBook[currentEdit].note > 24 and x > 1 and x < 2 + #addressBook[currentEdit].note:sub(25, 43))) and data == 1 then --# Note
- drawElement(2, 10, 1, 1, gray, black, addressBook[currentEdit].note:sub(1, 37))
- if #addressBook[currentEdit].note > 37 then
- drawElement(2, 11, 1, 1, nil, nil, addressBook[currentEdit].note:sub(38, 43))
- end
- bgCol, txtCol = black, white
- term.setTextColor(white)
- term.setCursorPos(2, 10)
- local newNote = read(nil, { addressBook[currentEdit].note }, 43)
- if newNote ~= "" and newNote ~= addressBook[currentEdit].note then
- addressBook[currentEdit].note = newNote
- gateDataChange()
- end
- drawElement(2, 10, 1, 1, white, black, addressBook[currentEdit].note:sub(1, 37) .. string.rep(" ", 38 - #addressBook[currentEdit].note:sub(1, 37)))
- if #addressBook[currentEdit].note > 24 then
- drawElement(2, 11, 1, 1, nil, nil, addressBook[currentEdit].note:sub(38, 43) .. string.rep(" ", 6 - #addressBook[currentEdit].note:sub(38, 43)))
- else
- drawElement(2, 11, 19, 1) --# clear line
- end
- elseif y == termY and data == 1 then --# Save / Close
- if x > math.floor(termX / 2) - 9 and x < math.floor(termX / 2) - 1 and gateChange then
- flashChoice((termX / 2) - 8, termY, 6, 1, white, green, "Save")
- drawElement((termX / 2) - 8, termY, 6, 1, silver, gray, "Save")
- saveData()
- elseif x > math.floor(termX / 2) + 1 and x < math.floor(termX / 2) + 9 then
- flashChoice((termX / 2) + 2, termY, 7, 1, white, red, "Close")
- runState = "Dial"
- drawElement(1, 4, termX - 1, termY - 3, nil, black) --# clear most of screen
- drawCLI()
- end
- end
- elseif runState == "Dial" then --# Dial (main screen)
- if y == 3 then --# Sub-header buttons (Sync, + , End Call)
- if x < math.floor(termX / 2) and host.ccDHD and data == 1 then --# Sync
- flashChoice(1, 3, (termX / 2) - 1, 1, cyan, white, "Sync")
- drawElement(1, 4, termX, termY - 2, nil, black) --# clear most of screen
- runState = "Sync"
- drawCLI()
- elseif x > math.floor(termX / 2) - 1 and x < math.floor(termX / 2) + 3 then --# '+' Add Gate / Quick-add Gate / Save Address Book
- if data == 1 then --# Add gate
- flashChoice(termX / 2, 3, 3, 1, green, white, " + ")
- addNewAddress()
- elseif data == 2 and gateChange then --# Save Address Book
- flashChoice(termX / 2, 3, 3, 1, green, white, " + ")
- drawElement(termX / 2, 3, 3, 1, white, green, " + ")
- saveData()
- elseif data == 3 then --# Quick-Add gate
- flashChoice(termX / 2, 3, 3, 1, green, white, " + ")
- drawElement(termX / 2, 3, 3, 1, orange, green, " + ")
- addNewAddress(true)
- end
- elseif x > math.floor(termX / 2) + 2 and host.id and (host.status == "Dialing" or host.status == "Connected" or dialAddress ~= "none") and data == 1 then --# End Call
- flashChoice((termX / 2) + 3, 3, 18, 1, red, white, "END Call")
- drawElement((termX / 2) + 3, 3, 18, 1, white, red, "END Call")
- remoteIrisState, dialAddress = "unk", "none"
- drawElement(6, 1, 1, 1, gray, blue, "I") --# 'I' for Iris control
- drawElement(22, 2, 9, 1, nil, gray) --# clear local gate status area
- drawAddressBook()
- netSend({ program = "ccDialer", gate = host.addr, command = "endCall" })
- end
- elseif y > 4 and y < 12 then --# Dial a listed address, view it's info, or delete it
- local xPos, yPos, magicNumber = 1, 5, ((gatePage - 1) * 12) + 1
- for i = magicNumber, math.min(abCount, gatePage * 12) do
- if x >= xPos and x <= xPos + 11 and y == yPos then
- if data == 1 and host.id and host.addr ~= addressBook[i].addr and host.status ~= "Connected" then --# Dial / Pause
- dialAddress = addressBook[i].addr
- flashDial(addressBook[i].name, xPos, yPos, i)
- netSend({ program = "ccDialer", gate = host.addr, command = dialAddress })
- drawAddressBook()
- elseif data == 2 then --# View/Edit
- flashDial(addressBook[i].name, xPos, yPos, i)
- drawGateLabels(i)
- elseif data == 3 and abCount > 1 then --# Delete entry if there is more than one entry in the address book
- flashDial(addressBook[i].name, xPos, yPos, i)
- if addressBook[i].addr == host.addr then host.name = host.addr drawHeader() end
- table.remove(addressBook, i)
- gateChange, abCount = true, abCount - 1
- gatePages = math.ceil(abCount / 12) --# Re-paginate AddressBook
- gatePage = math.min(gatePage, gatePages)
- if gatePage == gatePages then drawElement(2, 5, termX - 2, termY - 5, nil, black) end --# Clear address book area of screen
- drawElement((termX / 2) + 1, 3, 1, 1, orange, green, "+")
- drawAddressBook()
- drawNaviUI()
- end
- break
- else
- yPos = yPos + 2
- if yPos > 11 then xPos = xPos + 13 yPos = 5 end
- end
- end
- elseif y == termY and gatePages > 1 and data == 1 then --# Page Navigation via click
- if (x > 8 and x < 11 and gatePage > 1) or (x > 28 and x < 31 and gatePage < gatePages) then --# Home / End
- gatePage = x < 11 and 1 or gatePages
- if gatePage == gatePages then drawElement(1, 4, termX - 1, termY - 4, nil, black) end --# clear most of screen
- drawAddressBook()
- drawNaviUI()
- elseif (x > 11 and x < 13 and gatePage > 1) or (x > 26 and x < 28 and gatePage < gatePages) then --# Page Back / Page Forward
- gatePage = x < 13 and gatePage - 1 or gatePage + 1
- if gatePage == gatePages then drawElement(1, 4, termX - 1, termY - 4, nil, black) end --# clear most of screen
- drawAddressBook()
- drawNaviUI()
- elseif x > math.floor((termX - #pNum) / 2) and x < math.floor((termX + #pNum) / 2) + 1 and gatePages > 1 then --# Page Numbers (Go To Page dialogue)
- local pNumW = #pNum
- flashChoice(((termX - pNumW) / 2) + 1, termY, pNumW, 1, black, gray, pNum)
- drawElement(((termX - pNumW) / 2) + 1, termY, pNumW, 1, gray, black, pNum)
- drawGoPagePopUp()
- runState, bgCol, txtCol = "goPage", black, white
- term.setCursorPos(math.floor(termX / 2) - 1, termY - 2)
- local newPage = tonumber(read(nil, nil, 4)) --# user input
- drawElement((termX / 2) - 3, termY - 3, 8, 3) --# clear 'Page' dialogue
- gatePage = newPage or gatePage --# set gate page
- gatePage = math.floor(math.max(1, math.min(gatePage, gatePages)))
- if gatePage == gatePages and gatePages > 1 then drawElement(1, 4, termX - 1, termY - 4) end --# ...clear the data area if on the last page and there is more than one page
- runState = "Dial" --# restore runState
- drawAddressBook()
- drawNaviUI()
- end
- end
- elseif runState == "Sync" then --# Sync
- if x > 1 and x < termX and y > 3 and y < 13 and data == 1 then
- if y > 3 and y < 7 then --# Import
- flashChoice(2, 4, termX - 2, 3, green, white, "GET Address Book")
- netSend({ program = "ccDialer", gate = host.addr, command = "pull" })
- runState = "Dial"
- elseif y > 6 and y < 10 then --# Export
- flashChoice(2, 7, termX - 2, 3, orange, white, "SEND Address Book")
- netSend({ program = "ccDialer", gate = host.addr, command = "push", data = addressBook })
- runState = "Dial"
- elseif y > 9 and y < 13 then --# Cancel
- flashChoice(2, 10, termX - 2, 3, red, white, "C A N C E L")
- runState = "Dial"
- end
- if runState == "Dial" then
- drawElement(2, 4, termX - 1, termY - 3, nil, black) --# clear most of screen
- drawCLI()
- end
- end
- elseif runState == "selectHost" then --# Hosts list
- if x > 11 and x < 26 and y > 2 and y < 12 and data == 1 then
- local yPos, hostCount, magicNumber = 1, #hosts, ((syncPage - 1) * 5) + 1
- for i = magicNumber, math.min(hostCount, magicNumber + 4) do
- yPos = yPos + 2
- if y == yPos then
- flashChoice(12, yPos, 14, 1, black, white, hosts[i].name)
- runState, host.id, host.addr, host.ccDHD, host.name, host.status, remoteIrisState = "Dial", hosts[i].id, hosts[i].addr, hosts[i].ccDHD, hosts[i].name, hosts[i].gStatus, hosts[i].remoteIris
- pingTimer = os.startTimer(5)
- missedPings = 0
- drawElement(11, 2, 16, 11, nil, black) --# Clear hosts UI
- drawCLI()
- break
- end
- end
- end
- end
- elseif event == "mouse_scroll" then
- if runState == "Dial" and ((data == 1 and gatePage < gatePages) or (data == -1 and gatePage > 1)) then --# Address book
- gatePage = gatePage + data
- if gatePage == gatePages then drawElement(1, 5, termX - 1, termY - 5, nil, black) end --# clear address book area
- drawAddressBook()
- drawNaviUI()
- elseif runState == "viewing" and ((data == 1 and currentEdit < abCount) or (data == -1 and currentEdit > 1)) then --# View/Edit address book entry
- currentEdit = currentEdit + data
- drawHeader()
- drawGateData()
- elseif runState == "selectHost" and ((data == 1 and syncPage < syncPages) or (data == -1 and syncPage > 1)) then --# Hosts list
- syncPage = syncPage + data
- drawHostsUI()
- end
- elseif event == "key" then
- if data == keys.f1 and (runState == "Dial" or runState == "Help") then
- runState = runState == "Dial" and "Help" or "Dial"
- clearScreen(runState == "Dial" and black or white)
- drawCLI()
- elseif data == keys.pageUp or data == keys.pageDown then
- if runState == "Dial" and ((data == keys.pageUp and gatePage > 1) or (data == keys.pageDown and gatePage < gatePages)) then --# Address book
- gatePage = data == keys.pageUp and gatePage - 1 or gatePage + 1
- if gatePage == gatePages then drawElement(1, 4, termX - 1, termY - 4, nil, black) end --# clear data area of screen
- drawAddressBook()
- drawNaviUI()
- elseif runState == "selectHost" and ((data == keys.pageUp and syncPage > 1) or (data == keys.pageDown and syncPage < syncPages)) then --# Hosts list
- syncPage = data == keys.pageUp and syncPage - 1 or syncPage + 1
- drawHostsUI()
- end
- elseif (data == keys.home or data == keys["end"]) then
- if runState == "Dial" and ((data == keys.home and gatePage > 1) or (data == keys["end"] and gatePage < gatePages)) then --# Address book
- gatePage = data == keys.home and 1 or gatePages
- if gatePage == gatePages then drawElement(1, 4, termX - 1, termY - 4, nil, black) end --# clear data area of screen
- drawAddressBook()
- drawNaviUI()
- elseif runState == "selectHost" and ((data == keys.home and syncPage > 1) or (data == keys["end"] and syncPage < syncPages)) then --# Hosts list
- syncPage = data == keys.home and 1 or syncPages
- drawHostsUI()
- end
- end
- end
- end
- end
- local function dataPoller()
- local _, timer
- while true do
- _, timer = os.pullEvent("timer")
- if timer == pingTimer then
- missedPings = missedPings + 1
- if missedPings > 2 then
- if dialAddress ~= "none" then
- remoteIrisState, dialAddress = "unk", "none"
- if runState == "Sync" then
- runState = "Dial"
- drawElement(1, 5, termX - 1, termY - 5, nil, black) --# clear address book area
- end
- end
- host = { id = nil, name = "NO HOST", addr = "NO HOST", ccDHD = false, status = nil }
- pingTimer = nil
- missedPings = 0
- if runState == "Dial" or runState == "goPage" then
- drawHeader()
- drawCLI()
- if gettingInput then repositionCursor() end
- end
- elseif host.id then
- netSend({ program = "ccDialer", gate = host.addr, command = "ping" })
- pingTimer = os.startTimer(5)
- end
- end
- end
- end
- local function startupError(id)
- clearScreen()
- if id == "neural" then
- drawElement(2, 2, 1, 1, term.isColor() and red or white, black, "No Plethora neural")
- drawElement(2, 3, 1, 1, nil, nil, "interface detected!")
- drawElement(2, 5, 1, 1, nil, nil, "ccDialer REQUIRES a")
- drawElement(2, 6, 1, 1, nil, nil, "Plethora neural interface.")
- elseif id == "modem" then
- drawElement(2, 2, 1, 1, red, black, "No wireless")
- drawElement(2, 3, 1, 1, nil, nil, "modem detected!")
- drawElement(2, 5, 1, 1, nil, nil, "ccDialer REQUIRES")
- drawElement(2, 6, 1, 1, nil, nil, "a wireless modem.")
- elseif id == "resolution" then
- drawElement(2, 2, 1, 1, red, black, "Screen size")
- drawElement(2, 3, 1, 1, nil, nil, "is wrong!")
- drawElement(2, 5, 1, 1, nil, nil, "ccDialer REQUIRES")
- drawElement(2, 6, 1, 1, nil, nil, "39x12 or 39x13.")
- end
- term.setTextColor(white)
- term.setCursorPos(1, 8)
- end
- if not term.isColor() or pocket or turtle then return startupError("neural") end
- if termX ~= 39 or termY < 12 or termY > 13 then return startupError("resolution") end
- clearScreen()
- drawElement(9, 2, 22, 1, white, blue, "ccDialer Init...")
- drawElement(9, 3, 22, 9, nil, gray)
- local initSteps = {
- "Check CC label";
- "Hardware Discovery";
- "Ingest gate data";
- "Locate Sync host";
- }
- for i = 4, 10, 2 do
- drawElement(10, i, 1, 1, red, nil, "0")
- drawElement(12, i, 1, 1, silver, nil, initSteps[(i / 2) - 1])
- end
- drawElement(10, 4, 1, 1, orange, nil, "0")
- drawElement(12, 4, 1, 1, white, nil, "Check CC label")
- if not os.getComputerLabel() then os.setComputerLabel("Neural.cc#" .. thisCC) end
- drawElement(10, 4, 1, 1, green, nil, "O")
- drawElement(10, 6, 1, 1, orange, nil, "0")
- drawElement(12, 6, 1, 1, white, nil, "Hardware Discovery")
- for _, side in pairs(rs.getSides()) do
- if peripheral.isPresent(side) and peripheral.getType(side) == "modem" and peripheral.call(side, "isWireless") then
- rednet.open(side)
- modemSide = side
- break
- end
- end
- if modemSide == "none" then return startupError("modem") end
- drawElement(10, 6, 1, 1, green, nil, "O")
- drawElement(10, 8, 1, 1, orange, nil, "0")
- drawElement(12, 8, 1, 1, white, nil, "Ingest gate data")
- if not fs.exists("/data") then fs.makeDir("/data") end
- if fs.exists("/data/DHDgates") then
- addressBook[1] = nil
- local gateData = fs.open("/data/DHDgates", "r")
- addressBook = textutils.unserialize(gateData.readAll())
- gateData.close()
- abCount = #addressBook
- if addressBook[1].callDrop == nil then
- for i = 1, abCount do
- addressBook[i].callDrop = false
- end
- saveData()
- end
- end
- gatePages = math.max(1, math.ceil(abCount / 12))
- drawElement(10, 8, 1, 1, green, nil, "O")
- drawElement(10, 10, 1, 1, orange, nil, "0")
- drawElement(12, 10, 1, 1, white, nil, "Locate Sync host")
- findHosts()
- runState = "Dial"
- clearScreen()
- drawCLI()
- parallel.waitForAny(netReceive, userInput, dataPoller)
Add Comment
Please, Sign In to add comment