ComputerCraft Archive

== cc10 == (Basic X-10-like wireless control for redstone)

pocket networking unknown forum

Description

cc10 for Computercraft 1.6+ is a two+ piece solution that uses rednet for basic wireless control of redstone output on one or more computers (with wireless modems) or wireless turtles. What can you do...

Installation

Copy one of these commands into your ComputerCraft terminal:

Pastebin:pastebin get mbEHZZnh ==_cc10_==_(basic_x-10-like_wireless_control_for_redstone)
wget:wget https://pastebin.com/raw/mbEHZZnh ==_cc10_==_(basic_x-10-like_wireless_control_for_redstone)
Archive:wget https://cc.shobie.xyz/cc/get/pb-mbEHZZnh ==_cc10_==_(basic_x-10-like_wireless_control_for_redstone)
Quick Install: wget https://cc.shobie.xyz/cc/get/pb-mbEHZZnh == cc10 == (Basic X-10-like wireless control for redstone)

Usage

Run the program after downloading

Tags

networkingforumpocket-programs

Source

View Original Source

Code Preview

--[[    cc10 Remote    ]]--
--[[      by Dog       ]]--
--[[ aka HydrantHunter ]]--
--[[  with assistance  ]]--
--[[   from CometWolf  ]]--
--[[ pastebin mbEHZZnh ]]--
local cc10Rver = "2.0.04"
--[[
Tested with/requires:
  - Minecraft 1.6.4+
  - ComputerCraft 1.63+
    - 1 Advanced Wireless Pocket Computer
    - cc10host running on a Computer (standard or advanced) with a wireless modem
                       or a Wireless Turtle (standard or advanced)

Special thanks to: Anavrins   (pbkdf2/sha256 hashing)
                   SquidDev   (AES encryption/decryption)
                   Alex Kloss (base64 encoder/decoder)
]]--
--# AUTOMATIC/STATIC CONFIGURATION
--# Default Settings
local termX, termY = term.getSize()
local netSide, runState, thisCC = "none", "init", tostring(os.getComputerID())
local kernelState, timerDelay, inputting = true, false, false
local drawCLI, netReceive, userInput, pollTimer, tempState
--# Device List
local allDevices, filteredDevices = { }, { }
local numPages, pageNum = 1, 1
local uFilter = false
local thisDevice, cc10host
local cc10Types = {
  A = { "Analog", "UA" };
  UA = { "U-Analog", "D" };
  D = { "Digital", "UD" };
  UD = { "U-Digital", "PS" };
  PS = { "Pulsed-S", "PL" };
  PL = { "Pulsed-L", "P3" };
  P3 = { "Pulsed-3", "A" };
}
local colorBurst = {
  G = { order = 1, text = "Green", color = colors.green };
  S = { order = 2, text = "Sky", color = colors.lightBlue };
  B = { order = 3, text = "Blue", color = colors.blue };
  P = { order = 4, text = "Purple", color = colors.purple };
  R = { order = 5, text = "Red", color = colors.red };
  O = { order = 6, text = "Orange", color = colors.orange };
  N = { order = 7, text = "Brown", color = colors.brown };
  I = { order = 8, text = "Silver", color = colors.lightGray };
}
--# Color Definitions
local white = colors.white
local black = colors.black
local silver = colors.lightGray
local gray = colors.gray
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
--local colorX, word
--local curX, curY
--# END AUTOMATIC/STATIC CONFIGURATION

-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <[email protected]>
-- licensed under the terms of the LGPL2
-- http://lua-users.org/wiki/BaseSixtyFour
-- character table string
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-- encoding
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
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 = {}
  local 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
    if (operand == 1) then
      return 1
    end
    -- normal invert
    local exponent = ord - log[operand]
    return exp[exponent]
  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 i
      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 = {}
    local 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 = {}
    local 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
-- Pastebin: http://pastebin.com/6UV4qfNF
-- You may use this code in your projects without asking me, as long as credit is given and this header is kept intact
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 filterDeviceList()
  for i = #filteredDevices, 1, -1 do
    filteredDevices[i] = nil
  end
  local deviceCount = 0
  for i = 1, #allDevices do
    if (type(uFilter) == "boolean" and uFilter and allDevices[i].outputType:sub(1, 1) == "U") or (not uFilter) or (type(uFilter) == "string" and allDevices[i].outputType:sub(1, 1) ~= "U") then
      deviceCount = deviceCount + 1
      filteredDevices[deviceCount] = { }
      for k, v in pairs(allDevices[i]) do
        filteredDevices[deviceCount][k] = v
      end
      filteredDevices[deviceCount].adList = i
    end
  end
  numPages = math.max(1, math.ceil(#filteredDevices / 24))
  pageNum = math.min(pageNum, numPages)
end

do
  local function sortDeviceList(newSettings)
    local deviceCount = #allDevices
    if deviceCount == 0 then                --# the list is empty
      allDevices[1] = { }
      for k, v in pairs(newSettings) do     --# add this device
        allDevices[1][k] = v
      end
    else
      local nsName, filterCount = newSettings.name, #filteredDevices
      for i = 1, deviceCount do
        if cc10host == allDevices[i].cc then  --# we're already on the list
          for k, v in pairs(newSettings) do   --# update entry with current state of host
            allDevices[i][k] = v
          end
          for j = 1, filterCount do
            if filteredDevices[j].adList == i then
              for k, v in pairs(newSettings) do
                filteredDevices[j][k] = v
              end
              break
            end
          end
          return
        end
      end
      for i = 1, deviceCount do
        if nsName < allDevices[i].name then --# alphabetize
          table.insert(allDevices, i, newSettings) --# insert
          break
        end
        if i == deviceCount then            --# we've reached the end of the list
          deviceCount = deviceCount + 1
          allDevices[deviceCount] = { }     --# tack it on
          for k, v in pairs(newSettings) do
            allDevices[deviceCount][k] = v
          end
          break
        end
      end
    end
    filterDeviceList()
  end

  netReceive = function()
    local id, success, encKey, message, encryptedMessage, decryptedMessage, decodedMessage
    while true do
      if not rednet.isOpen(netSide) then rednet.open(netSide) end
      id, encryptedMessage = rednet.receive("cc10")
      if type(encryptedMessage) == "string" then
        success, decodedMessage = pcall(decode, encryptedMessage)
        if success and type(decodedMessage) == "string" then
          encKey = thisCC .. "cc!10" .. tostring(id)
          success, decryptedMessage = pcall(decrypt, encKey, decodedMessage)
          if success and type(decryptedMessage) == "string" then
            success, message = pcall(textutils.unserialize, decryptedMessage)
            if success and type(message) == "table" and message.program and message.program == "cc10host" then
              cc10host = id
              sortDeviceList(message)
              drawCLI()
            end
          end
        end
      end
    end
  end
end

local function netSend(targetHost, cmd1, act1)
  local dataPack = textutils.serialize({ program = "cc10", cmd = cmd1, action = act1 })
  if not rednet.isOpen(netSide) then rednet.open(netSide) end
  if targetHost == "ALL" then
    local encKey = thisCC .. "cc!10_Broadcast" .. thisCC
    local encryptedData = encode(encrypt(encKey, dataPack))
    rednet.broadcast(encryptedData, "cc10")
  else
    local encKey = tostring(targetHost) .. "cc!10" .. thisCC
    local encryptedData = encode(encrypt(encKey, dataPack))
    rednet.send(targetHost, encryptedData, "cc10")
  end
end

local function assignColor(assignment)
  return colorBurst[assignment].color or silver
end

local function drawElement(x, y, w, h, txtColor, bgColor, text)
  if type(w) == "boolean" then
    term.setCursorPos(x, y)
    term.setBackgroundColor(w and green or gray)
    term.write("  ")
    term.setBackgroundColor(w and gray or red)
    term.write("  ")
  else
    text = text and tostring(text) or ""
    local txtLen = #text
    w = math.max(w, txtLen)
    local spacer = (w - txtLen) / 2
    local txtLine = string.rep(" ", math.floor(spacer)) .. text .. string.rep(" ", math.ceil(spacer))
    if txtColor then term.setTextColor(txtColor) end
    if bgColor then term.setBackgroundColor(bgColor) end
    if h == 1 then
      term.setCursorPos(x, y)
      term.write(txtLine)
    else
      local line, textRow = string.rep(" ", w), y + math.floor(h / 2)
      for i = y, y + h - 1 do
        term.setCursorPos(x, i)         
        term.write(i == textRow and txtLine or line) --# Draw one line of the 'element' (box/rectangle/line-seg)
      end
    end
  end
end

local function drawHeader()
  local hColor = (runState == "Edit" or runState == "color") and assignColor(allDevices[thisDevice].color) or blue
  local hText = runState == "Help" and "cc10  " .. cc10Rver or ((runState == "Edit" or runState == "color") and allDevices[thisDevice].name .. " (cc# " .. tostring(allDevices[thisDevice].cc) .. ")" or "cc10")
  drawElement(1, 1, termX, 1, white, hColor, hText) --# title bar
  if runState == "List" or runState == "goPage" then
    drawElement(1, 1, 1, 1, nil, nil, "[ ]")
    local fColor = uFilter and green or gray
    drawElement(2, 1, 1, 1, type(uFilter) == "boolean" and fColor or orange, nil, "U")
    drawElement(termX - 2, 1, 1, 1, white, nil, "[ ]")
    drawElement(termX - 1, 1, 1, 1, red, nil, "X")
    drawElement(1, 2, 1, 1, green, gray, string.rep(" ", math.floor(termX / 4) - 2) .. "All ON" .. string.rep(" ", math.ceil(termX / 4) - 2))
    drawElement(math.ceil(termX / 2), 2, 1, 1, red, nil, string.rep(" ", math.floor(termX / 4) - 2) .. "All OFF" .. string.rep(" ", math.ceil(termX / 4) - 2))
  elseif runState == "Edit" or runState == "color" or runState == "Help" then
    drawElement(1, 2, termX, 1, red, gray, "Close")
  end
end

local function drawColorList(rating)
  local color
  drawElement(8, 4, 9, 8, nil, gray)                      --# menu body
  for k, v in pairs(colorBurst) do
    if rating == k then
      color = v.color
      drawElement(16, v.order + 3, 1, 1, nil, color, " ") --# selected color pip
    else
      color = silver
    end
    drawElement(7, v.order + 3, 1, 1, nil, v.color, " ")  --# rating color pips
    if rating == "I" and k == "I" then color = white end
    drawElement(9, v.order + 3, 1, 1, color, gray, v.text)
  end
end

local function viewDeviceEntry()
  runState, outputType, onState = "Edit", allDevices[thisDevice].outputType, allDevices[thisDevice].onState
  drawHeader()
  if thisDevice <= #allDevices then
    drawElement(2, 4, 1, 1, assignColor(allDevices[thisDevice].color), black, allDevices[thisDevice].name) --# Name
    drawElement(12, 4, 1, 1, gray, nil, "Power:") --# Power state
    drawElement(22, 4, allDevices[thisDevice].state ~= 0)
    drawElement(2, 6, 1, 1, gray, black, "cc#")   --# cc#
    drawElement(6, 6, 1, 1, white, nil, tostring(allDevices[thisDevice].cc))
    if outputType:sub(1, 1) ~= "P" then           --# Restore state upon restart ON/OFF
      drawElement(12, 6, 1, 1, gray, nil, "Restore:")
      drawElement(22, 6, allDevices[thisDevice].restore)
    else
      drawElement(12, 6, 14, 1, nil, black)
    end
    drawElement(2, 8, 1, 1, silver, black, allDevices[thisDevice].note) --# Short description
    drawElement(2, 10, 1, 1, gray, nil, "Output type:") --# Output Type (Digital, Analog, Universal Digital, Universal Analog, Pulsed (short), Pulsed (long), Pulsed (3))
    drawElement(15, 10, 1, 1, silver, nil, (cc10Types[outputType][1] or "ERROR  ") .. "   ")
    if outputType == "A" or outputType == "UA" then     --# Output Level (0-15)
      drawElement(2, 12, 1, 1, gray, nil, "Output Level:")
      local sState = onState < 10 and " " .. tostring(onState) or tostring(onState)
      drawElement(17, 12, 1, 1, white, nil, sState)
      drawElement(2, 13, 1, 1, silver, nil, "|---------------|")
      drawElement(onState + 2, 13, 1, 1, white, (onState > 0 and onState < 16) and green or red, "+")
    else
      drawElement(2, 12, 17, 2, nil, black)
    end
    if allDevices[thisDevice].loc.x ~= "No GPS Fix" then --# Device location (Export to pgps button)
      drawElement(14, 17, 9, 1, black, gray, "Export")
      drawElement(14, 18, 9, 1, nil, nil, "to pgps")
    end
    drawElement(2, 15, 1, 1, silver, black, "Dim:")      --# Device location (dim/x/y/z)
    drawElement(2, 16, 1, 1, nil, nil, "x:")
    drawElement(2, 17, 1, 1, nil, nil, "y:")
    drawElement(2, 18, 1, 1, nil, nil, "z:")
    drawElement(7, 15, 1, 1, brown, nil, allDevices[thisDevice].loc.dim)
    drawElement(5, 16, 1, 1, sky, nil, tostring(allDevices[thisDevice].loc.x))
    drawElement(5, 17, 1, 1, nil, nil, tostring(allDevices[thisDevice].loc.y))
    drawElement(5, 18, 1, 1, nil, nil, tostring(allDevices[thisDevice].loc.z))
  end
end

local function drawHelpUI()
  drawElement(1, 3, termX, termY - 2, nil, white)
  drawHeader()
  if tempState == "List" then
    drawElement(2, 4, 1, 1, black, white, "Left click an entry to")
    drawElement(2, 5, 1, 1, nil, nil, "toggle its state")
    drawElement(2, 7, 1, 1, nil, nil, "Right click an entry to")
    drawElement(2, 8, 1, 1, nil, nil, "view that entry's details")
    drawElement(2, 10, 1, 1, nil, nil, "Middle click an entry to")
    drawElement(2, 11, 1, 1, nil, nil, "lock it")
    drawElement(2, 13, 1, 1, nil, nil, "All ON/All OFF toggles")
    drawElement(2, 14, 1, 1, nil, nil, "all Universals in range")
    drawElement(2, 16, 1, 1, nil, nil, "[U] = Universal Filter")
    drawElement(2, 17, 1, 1, nil, nil, "Switch filter between")
    drawElement(2, 18, 1, 1, nil, nil, "universal, non-U, & all")
  elseif tempState == "Edit" then
    drawElement(2, 4, 1, 1, black, white, "Restore: restore last")
    drawElement(2, 5, 1, 1, nil, nil, "state after server or")
    drawElement(2, 6, 1, 1, nil, nil, "software restart")
    drawElement(2, 8, 1, 1, nil, nil, "Left click name, note, ")
    drawElement(2, 9, 1, 1, nil, nil, "or dim to edit")
    drawElement(2, 11, 1, 1, nil, nil, "Right click name to")
    drawElement(2, 12, 1, 1, nil, nil, "change color")
    drawElement(2, 14, 1, 1, nil, nil, "Universal Digital and")
    drawElement(2, 15, 1, 1, nil, nil, "Universal Analog both")
    drawElement(2, 16, 1, 1, nil, nil, "respond to All ON/OFF")
  end
end

local function drawNaviUI()
  local pNum = tostring(pageNum)
  if pageNum < 100 then pNum = "0" .. pNum end --# Add a "0" before double digit page numbers
  if pageNum < 10 then pNum = "0" .. pNum end  --# Add another "0" before single digit page numbers
  drawElement(math.floor(termX / 2) - 10, termY, 7, 1, gray, black, pageNum > 1 and "<< BACK" or "       ")
  drawElement(math.floor(termX / 2) + 4, termY, 7, 1, nil, nil, pageNum < numPages and "NEXT >>" or "       ")
  drawElement(math.floor((termX - #pNum) / 2) + 1, termY, 3, 1, silver, nil, pNum) --# Page Number
end

local function drawMainUI() --# Device 'Address Book'
  local xPos, yPos, fdName, fdState, spacer = 1, 4
  local magicNumber = ((pageNum - 1) * 23) + pageNum
  for i = magicNumber, math.min(#filteredDevices, pageNum * 24) do
    term.setCursorPos(xPos, yPos)
    fdState = filteredDevices[i].state
    if not filteredDevices[i].lockState then
      term.setBackgroundColor(fdState == 0 and black or assignColor(filteredDevices[i].color))
      term.setTextColor(fdState == 0 and assignColor(filteredDevices[i].color) or white)
    else
      term.setBackgroundColor(fdState == 0 and black or gray)
      term.setTextColor(fdState == 0 and gray or silver)
    end
    fdName = filteredDevices[i].name
    spacer = (8 - #fdName) / 2
    term.write(string.rep(" ", math.floor(spacer)) .. fdName .. string.rep(" ", math.ceil(spacer)))
    yPos = yPos + 2
    if yPos > 19 then yPos = 4 xPos = xPos + 9 end
  end
end

do
  local runStates = {
    List = function() drawMainUI() drawNaviUI() end;
    Edit = function() viewDeviceEntry() end;
  }

  drawCLI = function() --# Client Interface 'decider'
    if not inputting then
      drawHeader()
      if runStates[runState] then runStates[runState]() end
    end
  end
end

local function readInput(cX, cY, cO, bG, limit, mask) --# cursor X, Y, text color, bg color, character limit, character mask
  local word, pos = "", 0
  local curX, curY = cX, cY
  inputting, timerDelay = true, true
  os.cancelTimer(pollTimer)
  bG = bG or black
  limit = (limit and type(limit) == "number" and limit > 0) and limit or 21
  term.setCursorPos(curX, curY)
  term.setTextColor(cO)
  term.setBackgroundColor(bG)
  term.setCursorBlink(true)
  while true do
    local event, data, mX, mY = os.pullEvent()
    if event == "key" then
      if data == keys.backspace then
        if curX > cX then
          curX = math.max(cX, curX - 1)
          pos = math.max(0, pos - 1)
          word = word:sub(1, #word - 1)
        end
        drawElement(curX, curY, 1, 1, cO, bG, " ")
        term.setCursorPos(curX, curY)
      elseif data == keys.enter or data == keys.numPadEnter then
        break
      end
    elseif event == "char" and pos < limit then
      drawElement(curX, curY, 1, 1, cO, bG, mask or data)
      word = word .. data
      curX = curX + 1
      pos = pos + 1
    elseif event == "paste" and pos < limit then
      if pos + #data > limit then
        data = data:sub(1, limit - pos)
      end
      drawElement(curX, curY, 1, 1, cO, bG, mask and string.rep(mask, #data) or data)
      word = word .. data
      curX = curX + #data
      pos = pos + #data
    elseif event == "mouse_click" then
      if mY ~= curY or mX < cX or mX >= cX + limit then
        break
      end
    end
  end
  term.setCursorBlink(false)
  inputting = false
  return word
end

do
  local function toggleFilter()
    if type(uFilter) == "boolean" then
      uFilter = uFilter and "U" or true
    else
      uFilter = false
    end
    filterDeviceList()
    drawElement(1, 3, termX, termY - 2, nil, black)
    drawCLI()
  end

  local function inputMain()
    local event, mButton, mcX, mcY, xPos, yPos, magicNumber
    while true do
      event, mButton, mcX, mcY = os.pullEvent()
      if event == "mouse_click" then
        if runState == "Help" then
          if mcY == 2 and mButton == 1 then
            drawElement(1, 2, termX, termY - 1, nil, black)
            runState = tempState
            drawCLI()
          end
        elseif runState == "Edit" then
          if mcY == 2 and mButton == 1 then --# Exit Edit Screen
            drawElement(2, 2, termX, termY - 1, nil, black)
            runState = "List"
            drawCLI()
          elseif mcY == 4 then
            if mcX > 1 and mcX < 10 then
              if mButton == 1 then          --# Edit Device Name
                runState = "EditName"
                return
              elseif mButton == 2 then      --# Change color assignment
                drawColorList(allDevices[thisDevice].color)
                runState = "color"
                return
              end
            elseif mcX > 21 and mcX < 26  and mButton == 1 then --# Change ON/OFF state
              if allDevices[thisDevice].state == 0 then
                netSend(allDevices[thisDevice].cc, "set", allDevices[thisDevice].onState)
              elseif allDevices[thisDevice].state > 0 and allDevices[thisDevice].state < 16 then
                netSend(allDevices[thisDevice].cc, "set", 0)
              end
            end
          elseif mcY == 6 and mButton == 1 then
            if mcX > 21 and mcX < 26 and allDevices[thisDevice].outputType:sub(1, 1) ~= "P" then --# Restore state on restart?
              netSend(allDevices[thisDevice].cc, "restore", not allDevices[thisDevice].restore)
            end
          elseif mcY == 8 and mButton == 1 then  --# Edit Device Note
            if mcX > 1 then
              runState = "EditNote"
              return
            end
          elseif mcY == 10 and mButton == 1 then --# Edit Output Type (Analog/Digital/Universal/Pulsed)
            if mcX > 14 and mcX < 24 then
              netSend(allDevices[thisDevice].cc, "changeOutputType", cc10Types[allDevices[thisDevice].outputType][2])
            end
          elseif mcY == 13 and mButton == 1 then --# Edit Redstone Output Level
            if mcX > 2 and mcX < 18 then
              if allDevices[thisDevice].outputType == "A" or allDevices[thisDevice].outputType == "UA" then
                netSend(allDevices[thisDevice].cc, "lvl", mcX - 2)
              end
            end
          elseif mcY == 15 and mButton == 1 then --# Device Dimension
            if mcX > 6 and mcX < 26 then
              runState = "EditDim"
              return
            end
          elseif (mcY == 17 or mcY == 18) and mButton == 1 then
            if mcX > 14 and mcX < 23 and allDevices[thisDevice].loc.x ~= "No GPS Fix" then --# Export to Lyqyd's pocketgps
              drawElement(14, 17, 9, 1, white, gray, "Export")
              drawElement(14, 18, 9, 1, nil, nil, "to pgps")
              sleep(0.1)
              drawElement(14, 17, 9, 1, black, nil, "Export")
              drawElement(14, 18, 9, 1, nil, nil, "to pgps")
              local pgpsPlaces = fs.open("/.places", fs.exists("/.places") and "a" or "w")
              pgpsPlaces.writeLine(allDevices[thisDevice].loc.x .. ", " .. allDevices[thisDevice].loc.y .. ", " .. allDevices[thisDevice].loc.z .. ", " .. allDevices[thisDevice].name)
              pgpsPlaces.close()
            end
          end
        elseif runState == "List" then
          if mcY == 1 and mButton == 1 then
            if mcX > 0 and mcX < 4 then --# Universal filter
              toggleFilter()
            elseif mcX > termX - 3 and mcX <= termX then --# Exit cc10 Remote
              kernelState = false
              if rednet.isOpen(netSide) then rednet.close(netSide) end
              term.setBackgroundColor(black)
              term.setTextColor(white)
              term.clear()
              term.setCursorPos(1, 1)
              break
            end
          elseif mcY == 2 and mButton == 1 then --# ALL ON/OFF
            netSend("ALL", "set", mcX < termX / 2 and "ON" or "OFF")
          elseif mcY > 3 and mcY < 19 then --# Switch a listed device on or off, or view it's info
            magicNumber, xPos, yPos = ((pageNum - 1) * 23) + pageNum, 1, 4
            for i = magicNumber, math.min(#filteredDevices, pageNum * 24) do
              if mcX >= xPos and mcX < xPos + 8 and mcY == yPos then
                if not filteredDevices[i].lockState then
                  if mButton == 1 then
                    netSend(filteredDevices[i].cc, "set", filteredDevices[i].state == 0 and filteredDevices[i].onState or 0)
                    drawMainUI()
                  elseif mButton == 2 then
                    term.clear()
                    thisDevice = filteredDevices[i].adList
                    viewDeviceEntry()
                  elseif mButton == 3 then
                    thisDevice = filteredDevices[i].adList
                    netSend(filteredDevices[i].cc, "lock")
                  end
                else
                  thisDevice = filteredDevices[i].adList
                  runState = "unlock"
                  return
                end
              end
              yPos = yPos + 2
              if yPos > 19 then yPos = 4 xPos = xPos + 9 end
            end
          elseif mcY == termY and numPages > 1 and mButton == 1 then --# Page Navigation via click
            if mcX < math.floor(termX / 2) - 3 or mcX > math.floor(termX / 2) + 3 then --# Page Back / Forward
              pageNum = mcX < math.floor(termX / 2) - 3 and math.max(1, pageNum - 1) or math.min(pageNum + 1, numPages)
              if pageNum == numPages and numPages > 1 then drawElement(2, 4, termX, termY - 3, nil, black) end
              drawMainUI()
              drawNaviUI()
            elseif mcX > math.floor(termX / 2) - 2 and mcX < math.floor(termX / 2) + 2 then --# Page Numbers (Go To Page dialogue)
              runState = "goPage"
              return
            end
          end
        end
      elseif event == "mouse_scroll" and runState == "List" then
        pageNum = mButton == 1 and math.min(pageNum + 1, numPages) or math.max(1, pageNum - 1)
        if pageNum == numPages and numPages > 1 then drawElement(2, 4, termX, termY - 3, nil, black) end
        drawMainUI()
        drawNaviUI()
      elseif event == "key" then
        if mButton == keys.u and runState == "List" then --# U
          if runState == "List" then toggleFilter() end
        elseif mButton == keys.f1 then
          if runState ~= "Help" then
            tempState = runState
            runState = "Help"
            drawHelpUI()
          else
            drawElement(1, 2, termX, termY - 1, nil, black)
            runState = tempState
            drawCLI()
          end
        end
      end
    end
  end

  local function colorPicker()
    local choice = false
    local _, button, x, y = os.pullEvent("mouse_click")
    if x > 6 and x < 17 and y > 3 and y < 12 and button == 1 then
      for colorChoice = 4, 11 do
        if y == colorChoice then
          for k, v in pairs(colorBurst) do
            if (colorChoice - 3) == v.order then
              netSend(allDevices[thisDevice].cc, "color", k)
              choice = true
              break
            end
          end
        end
        if choice then break end
      end
    end
    drawElement(7, 4, 10, 8, nil, black)
    viewDeviceEntry()
  end

  local function editEntry()
    if runState == "EditName" then
      drawElement(2, 4, 1, 1, gray, black, allDevices[thisDevice].name)
      local newName = readInput(2, 4, yellow, nil, 8)
      if newName ~= "" then
        netSend(allDevices[thisDevice].cc, "name", newName)
        drawElement(2, 4, 8, 1)
      else
        drawElement(2, 4, 1, 1, assignColor(allDevices[thisDevice].color), nil, allDevices[thisDevice].name)
      end
    elseif runState == "EditNote" then
      drawElement(2, 8, 1, 1, gray, black, allDevices[thisDevice].note)
      local newNote = readInput(2, 8, white, nil, 21)
      if newNote ~= "" then
        netSend(allDevices[thisDevice].cc, "note", newNote)
        drawElement(2, 8, 21, 1)
      else
        drawElement(2, 8, 1, 1, silver, nil, allDevices[thisDevice].note)
      end
    elseif runState == "EditDim" then
      drawElement(7, 15, 1, 1, gray, black, allDevices[thisDevice].loc.dim)
      local newDim = readInput(7, 15, sky, nil, 19)
      if newDim ~= "" then
        netSend(allDevices[thisDevice].cc, "locDim", newDim)
        drawElement(7, 15, 19, 1)
      else
        drawElement(7, 15, 1, 1, brown, nil, allDevices[thisDevice].loc.dim)
      end
    end
    runState = "Edit"
  end

  local function enterPassword()
    drawElement(math.floor(termX / 2) - 7, 8, 15, 1, white, gray, "   Password:   ")
    drawElement(math.floor(termX / 2) - 7, 9, 15, 2)
    drawElement(math.floor(termX / 2) - 6, 9, 13, 1, nil, black) --# input area bg
    local pass = readInput(math.floor(termX / 2) - 6, 9, lime, nil, 13, "*")
    if pass ~= "" then
      local pw = table.concat(pbkdf2(pass, "cc!10", 15)) 
      netSend(allDevices[thisDevice].cc, "unlock", pw)
    end
    drawElement(math.floor(termX / 2) - 7, 8, 15, 3)
    runState = "List"
    drawMainUI()
  end

  local function goToPage()
    drawElement(math.floor(termX / 2) - 3, termY - 3, 7, 1, white, gray, "Page:")
    drawElement(math.floor(termX / 2) - 3, termY - 2, 7, 2)
    drawElement(math.floor(termX / 2) - 2, termY - 2, 5, 1, nil, black) --# input area bg
    local newPage = tonumber(readInput(math.floor(termX / 2) - 1, termY - 2, lime, nil, 3))
    pageNum = newPage or pageNum
    pageNum = math.max(1, math.min(pageNum, numPages))
    if pageNum == numPages and numPages > 1 then
      drawElement(1, 4, termX, termY - 3)
    else
      drawElement(math.floor(termX / 2) - 3, termY - 3, 7, 3)
    end
    runState = "List"
    drawMainUI()
    drawNaviUI()
  end

  local runStates = {
    List = function() inputMain() end;
    Edit = function() inputMain() end;
    Help = function() inputMain() end;
    EditName = function() editEntry() end;
    EditNote = function() editEntry() end;
    EditDim = function() editEntry() end;
    unlock = function() enterPassword() end;
    goPage = function() goToPage() end;
    color = function() colorPicker() end;
  }

  userInput = function()
    repeat
      if runStates[runState] then runStates[runState]() end
      if kernelState and timerDelay and not inputting then
        timerDelay = false
        pollTimer = os.startTimer(0.1)
      end
    until not kernelState
  end
end

local function dataPoller()
  local removal, _, timerID = false
  while true do
    _, timerID = os.pullEvent("timer")
    if timerID == pollTimer then
      if runState == "List" then
        for i = #allDevices, 1, -1 do
          if allDevices[i].quietCount > 1 then
            table.remove(allDevices, i)
            removal = true
          else
            allDevices[i].quietCount = allDevices[i].quietCount + 1
          end
        end
        if removal then
          removal = false
          filterDeviceList()
          drawElement(1, 4, termX, termY - 3, nil, black)
          drawMainUI()
          drawNaviUI()
        end
      end
      if not timerDelay then
        netSend("ALL", "QRY", "cc10QRY")
        pollTimer = os.startTimer(3)
      end
    end
  end
end

term.setBackgroundColor(black)
term.clear()
if not term.isColor() or not pocket then term.setCursorPos(1, 1) error("Advanced Wireless Pocket Computer Required", 0) end
drawElement(2, 2, 1, 1, white, nil, "Initializing . . .")
if not os.getComputerLabel() then os.setComputerLabel("PocketPC.ID#" .. thisCC) end
if peripheral.isPresent("back") and peripheral.getType("back") == "modem" and peripheral.call("back", "isWireless") then
  rednet.open("back")
  netSide = "back"
else
  term.clear()
  drawElement(2, 2, 1, 1, red, nil, "No wireless")
  drawElement(2, 3, 1, 1, nil, nil, "modem detected!")
  drawElement(2, 5, 1, 1, nil, nil, "cc10 REQUIRES")
  drawElement(2, 6, 1, 1, nil, nil, "a wireless modem.")
  term.setCursorPos(1, 9)
  return
end
runState = "List"
drawElement(2, 4, 1, 1, white, nil, "Searching for hosts . . .")
netSend("ALL", "QRY", "cc10QRY")
pollTimer = os.startTimer(3)
term.clear()
drawCLI()
parallel.waitForAny(userInput, netReceive, dataPoller)