View difference between Paste ID: 5WJWrTXt and jtFa7V1n
SHOW: | | - or go back to the newest paste.
1
--[[    WiRe Client    ]]--
2
--[[      by Dog       ]]--
3
--[[ aka HydrantHunter ]]--
4
--[[  Wi = Wireless    ]]--
5
--[[  Re = Redstone    ]]--
6
--[[ pastebin jtFa7V1n ]]--
7
local WiReCver = "2.0.05"
8
--[[
9
Tested with/requires:
10
  - Minecraft 1.6.4+
11
  - ComputerCraft 1.63+
12
    - A Computer (standard or advanced) with a modem and, optionally, one or more Advanced Monitors
13
    - WiRe Server running on a computer (standard or advanced) with a modem and one advanced monitor array
14
15
Special thanks to: SquidDev   (AES encryption/decryption)
16
                   Alex Kloss (base64 encoder/decoder)
17
]]--
18
local tArgs = { ... }
19
--# CONFIGURATION
20
--# Default Settings
21
local termX, termY = term.getSize()
22
local thisCC = tostring(os.getComputerID())
23
local config = "/data/WiReClientCfg"
24
local ccSettings = {
25
  name = "WiReClient";  --# this client's name
26
  note = "short note";  --# short note/description
27
  color = "Silver";     --# network group
28
  side = "top";         --# redstone side
29
  deviceType = "Door";  --# device type
30
  autoClose = true;     --# automatically close after being opened
31
  autoDelay = 3;        --# autoClose delay
32
  onState = false;      --# ON/CLOSED - redstone output
33
  offState = true;      --# OFF/OPEN - redstone output
34
  defaultStart = false; --# startup redstone output
35
  lastState = false;    --# last redstone state (for defaultStart = "last")
36
  lockState = false;    --# lock state
37
  getGPSFix = true;     --# get GPS fix on startup
38
  newColors = true;     --# using new color names
39
}
40
local validStates = {
41
  ON = "OFF";
42
  OFF = "ON";
43
  OPEN = "CLOSED";
44
  CLOSED = "OPEN";
45
  LOCKED = true;
46
  UNLOCK = true;
47
  WiReQRY = true;
48
}
49
local mon, loc = { }, { }
50
local deviceState, deviceType, autoDelay = "QRY", "QRY", 0
51
local defaultStart, onState, offState, defaultLock, defaultUnlock = false, false, true, false, false
52
local kernelState, ccSuccess, autoClose, help = false, false, false, false
53
local network, server, modemSide, thisCommand, pollTimer, updateScreens, staticTermScreen
54
--# Terminal Colors
55
local white = colors.white
56
local black = colors.black
57
local silver = colors.lightGray
58
local gray = colors.gray
59
local brown = colors.brown
60
local yellow = colors.yellow
61
local orange = colors.orange
62
local red = colors.red
63
local magenta = colors.magenta
64
local purple = colors.purple
65
local blue = colors.blue
66
local sky = colors.lightBlue
67
local cyan = colors.cyan
68
local lime = colors.lime
69
local green = colors.green
70
if not term.isColor() then
71
  silver = colors.white
72
  gray = colors.black
73
  brown = colors.white
74
  yellow = colors.white
75
  orange = colors.white
76
  red = colors.white
77
  magenta = colors.white
78
  purple = colors.white
79
  green = colors.white
80
  blue = colors.black
81
  sky = colors.white
82
  cyan = colors.white
83
  lime = colors.white
84
  green = colors.white
85
end
86
--# Monitor colors
87
local mwhite = colors.white
88
local mblack = colors.black
89
local msilver = colors.lightGray
90
local mgray = colors.gray
91
local mbrown = colors.brown
92
local myellow = colors.yellow
93
local morange = colors.orange
94
local mred = colors.red
95
local mmagenta = colors.magenta
96
local mpurple = colors.purple
97
local mblue = colors.blue
98
local msky = colors.lightBlue
99
local mcyan = colors.cyan
100
local mlime = colors.lime
101
local mgreen = colors.green
102
--# Status colors
103
local switchStates = {
104
  OPEN = { orange, morange, " [ ] " };
105
  CLOSED = { green, mgreen, " [O] " };
106
  ON = { green, mgreen, " [O] " };
107
  OFF = { orange, morange, " [ ] " };
108
}
109
--# END CONFIGURATION
110
111
-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
112
-- licensed under the terms of the LGPL2
113
-- http://lua-users.org/wiki/BaseSixtyFour
114
-- character table string
115
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
116
-- encoding
117
function encode(data)
118
  return ((data:gsub('.', function(x) 
119
    local r,b='',x:byte()
120
    for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
121
    return r;
122
  end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
123
    if (#x < 6) then return '' end
124
    local c=0
125
    for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
126
    return b:sub(c+1,c+1)
127
  end)..({ '', '==', '=' })[#data%3+1])
128
end
129
-- decoding
130
function decode(data)
131
  data = string.gsub(data, '[^'..b..'=]', '')
132
  return (data:gsub('.', function(x)
133
    if (x == '=') then return '' end
134
    local r,f='',(b:find(x)-1)
135
    for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
136
    return r;
137
  end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
138
    if (#x ~= 8) then return '' end
139
    local c=0
140
    for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
141
    return string.char(c)
142
  end))
143
end
144
145
-- AES Lua implementation by SquidDev
146
-- https://gist.github.com/SquidDev/86925e07cbabd70773e53d781bd8b2fe
147
local encrypt, decrypt
148
do
149
  local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
150
  local bit=_W(function(_ENV, ...)
151
  --[[
152
    This bit API is designed to cope with unsigned integers instead of normal integers
153
    To do this we add checks for overflows: (x > 2^31 ? x - 2 ^ 32 : x)
154
    These are written in long form because no constant folding.
155
  ]]
156
  local floor = math.floor
157
  local lshift, rshift
158
159
  rshift = function(a,disp)
160
    return floor(a % 4294967296 / 2^disp)
161
  end
162
163
  lshift = function(a,disp)
164
    return (a * 2^disp) % 4294967296
165
  end
166
167
  return {
168
    -- bit operations
169
    bnot = bit32 and bit32.bnot or bit.bnot,
170
    band = bit32 and bit32.band or bit.band,
171
    bor  = bit32 and bit32.bor or bit.bor,
172
    bxor = bit32 and bit32.bxor or bit.bxor,
173
    rshift = rshift,
174
    lshift = lshift,
175
  }
176
  end)
177
178
  local gf=_W(function(_ENV, ...)
179
  -- finite field with base 2 and modulo irreducible polynom x^8+x^4+x^3+x+1 = 0x11d
180
  local bxor = bit32 and bit32.bxor or bit.bxor
181
  local lshift = bit.lshift
182
  -- private data of gf
183
  local n = 0x100
184
  local ord = 0xff
185
  local irrPolynom = 0x11b
186
  local exp = {}
187
  local log = {}
188
  --
189
  -- add two polynoms (its simply xor)
190
  --
191
  local function add(operand1, operand2)
192
    return bxor(operand1,operand2)
193
  end
194
  --
195
  -- subtract two polynoms (same as addition)
196
  --
197
  local function sub(operand1, operand2)
198
    return bxor(operand1,operand2)
199
  end
200
  --
201
  -- inverts element
202
  -- a^(-1) = g^(order - log(a))
203
  --
204
  local function invert(operand)
205
    -- special case for 1
206
    if (operand == 1) then
207
      return 1
208
    end
209
    -- normal invert
210
    local exponent = ord - log[operand]
211
    return exp[exponent]
212
  end
213
  --
214
  -- multiply two elements using a logarithm table
215
  -- a*b = g^(log(a)+log(b))
216
  --
217
  local function mul(operand1, operand2)
218
    if (operand1 == 0 or operand2 == 0) then
219
      return 0
220
    end
221
    local exponent = log[operand1] + log[operand2]
222
    if (exponent >= ord) then
223
      exponent = exponent - ord
224
    end
225
    return exp[exponent]
226
  end
227
  --
228
  -- divide two elements
229
  -- a/b = g^(log(a)-log(b))
230
  --
231
  local function div(operand1, operand2)
232
    if (operand1 == 0)  then
233
      return 0
234
    end
235
    -- TODO: exception if operand2 == 0
236
    local exponent = log[operand1] - log[operand2]
237
    if (exponent < 0) then
238
      exponent = exponent + ord
239
    end
240
    return exp[exponent]
241
  end
242
  --
243
  -- print logarithmic table
244
  --
245
  local function printLog()
246
    for i = 1, n do
247
      print("log(", i-1, ")=", log[i-1])
248
    end
249
  end
250
  --
251
  -- print exponentiation table
252
  --
253
  local function printExp()
254
    for i = 1, n do
255
      print("exp(", i-1, ")=", exp[i-1])
256
    end
257
  end
258
  --
259
  -- calculate logarithmic and exponentiation table
260
  --
261
  local function initMulTable()
262
    local a = 1
263
    for i = 0,ord-1 do
264
      exp[i] = a
265
      log[a] = i
266
      -- multiply with generator x+1 -> left shift + 1
267
      a = bxor(lshift(a, 1), a)
268
      -- if a gets larger than order, reduce modulo irreducible polynom
269
      if a > ord then
270
        a = sub(a, irrPolynom)
271
      end
272
    end
273
  end
274
275
  initMulTable()
276
277
  return {
278
    add = add,
279
    sub = sub,
280
    invert = invert,
281
    mul = mul,
282
    div = div,
283
    printLog = printLog,
284
    printExp = printExp,
285
  }
286
  end)
287
288
  util=_W(function(_ENV, ...)
289
  -- Cache some bit operators
290
  local bxor = bit.bxor
291
  local rshift = bit.rshift
292
  local band = bit.band
293
  local lshift = bit.lshift
294
  local sleepCheckIn
295
  --
296
  -- calculate the parity of one byte
297
  --
298
  local function byteParity(byte)
299
    byte = bxor(byte, rshift(byte, 4))
300
    byte = bxor(byte, rshift(byte, 2))
301
    byte = bxor(byte, rshift(byte, 1))
302
    return band(byte, 1)
303
  end
304
  --
305
  -- get byte at position index
306
  --
307
  local function getByte(number, index)
308
    return index == 0 and band(number,0xff) or band(rshift(number, index*8),0xff)
309
  end
310
  --
311
  -- put number into int at position index
312
  --
313
  local function putByte(number, index)
314
    return index == 0 and band(number,0xff) or lshift(band(number,0xff),index*8)
315
  end
316
  --
317
  -- convert byte array to int array
318
  --
319
  local function bytesToInts(bytes, start, n)
320
    local ints = {}
321
    for i = 0, n - 1 do
322
      ints[i + 1] =
323
          putByte(bytes[start + (i*4)], 3) +
324
          putByte(bytes[start + (i*4) + 1], 2) +
325
          putByte(bytes[start + (i*4) + 2], 1) +
326
          putByte(bytes[start + (i*4) + 3], 0)
327
      if n % 10000 == 0 then sleepCheckIn() end
328
    end
329
    return ints
330
  end
331
  --
332
  -- convert int array to byte array
333
  --
334
  local function intsToBytes(ints, output, outputOffset, n)
335
    n = n or #ints
336
    for i = 0, n - 1 do
337
      for j = 0,3 do
338
        output[outputOffset + i*4 + (3 - j)] = getByte(ints[i + 1], j)
339
      end
340
      if n % 10000 == 0 then sleepCheckIn() end
341
    end
342
    return output
343
  end
344
  --
345
  -- convert bytes to hexString
346
  --
347
  local function bytesToHex(bytes)
348
    local hexBytes = ""
349
    for i,byte in ipairs(bytes) do
350
      hexBytes = hexBytes .. string.format("%02x ", byte)
351
    end
352
    return hexBytes
353
  end
354
355
  local function hexToBytes(bytes)
356
    local out = {}
357
    for i = 1, #bytes, 2 do
358
      out[#out + 1] = tonumber(bytes:sub(i, i + 1), 16)
359
    end
360
    return out
361
  end
362
  --
363
  -- convert data to hex string
364
  --
365
  local function toHexString(data)
366
    local type = type(data)
367
    if (type == "number") then
368
      return string.format("%08x",data)
369
    elseif (type == "table") then
370
      return bytesToHex(data)
371
    elseif (type == "string") then
372
      local bytes = {string.byte(data, 1, #data)}
373
      return bytesToHex(bytes)
374
    else
375
      return data
376
    end
377
  end
378
379
  local function padByteString(data)
380
    local dataLength = #data
381
    local random1 = math.random(0,255)
382
    local random2 = math.random(0,255)
383
    local prefix = string.char(random1,
384
      random2,
385
      random1,
386
      random2,
387
      getByte(dataLength, 3),
388
      getByte(dataLength, 2),
389
      getByte(dataLength, 1),
390
      getByte(dataLength, 0)
391
    )
392
    data = prefix .. data
393
    local padding, paddingLength = "", math.ceil(#data/16)*16 - #data
394
    for i=1,paddingLength do
395
      padding = padding .. string.char(math.random(0,255))
396
    end
397
    return data .. padding
398
  end
399
400
  local function properlyDecrypted(data)
401
    local random = {string.byte(data,1,4)}
402
    if (random[1] == random[3] and random[2] == random[4]) then
403
      return true
404
    end
405
    return false
406
  end
407
408
  local function unpadByteString(data)
409
    if (not properlyDecrypted(data)) then
410
      return nil
411
    end
412
    local dataLength = putByte(string.byte(data,5), 3)
413
             + putByte(string.byte(data,6), 2)
414
             + putByte(string.byte(data,7), 1)
415
             + putByte(string.byte(data,8), 0)
416
    return string.sub(data,9,8+dataLength)
417
  end
418
419
  local function xorIV(data, iv)
420
    for i = 1,16 do
421
      data[i] = bxor(data[i], iv[i])
422
    end
423
  end
424
425
  local function increment(data)
426
    local i = 16
427
    while true do
428
      local value = data[i] + 1
429
      if value >= 256 then
430
        data[i] = value - 256
431
        i = (i - 2) % 16 + 1
432
      else
433
        data[i] = value
434
        break
435
      end
436
    end
437
  end
438
439
  -- Called every encryption cycle
440
  local push, pull, time = os.queueEvent, coroutine.yield, os.time
441
  local oldTime = time()
442
  local function sleepCheckIn()
443
    local newTime = time()
444
    if newTime - oldTime >= 0.03 then -- (0.020 * 1.5)
445
      oldTime = newTime
446
      push("sleep")
447
      pull("sleep")
448
    end
449
  end
450
451
  local function getRandomData(bytes)
452
    local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
453
    local result = {}
454
    for i=1,bytes do
455
      insert(result, random(0,255))
456
      if i % 10240 == 0 then sleep() end
457
    end
458
    return result
459
  end
460
461
  local function getRandomString(bytes)
462
    local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
463
    local result = {}
464
    for i=1,bytes do
465
      insert(result, char(random(0,255)))
466
      if i % 10240 == 0 then sleep() end
467
    end
468
    return table.concat(result)
469
  end
470
471
  return {
472
    byteParity = byteParity,
473
    getByte = getByte,
474
    putByte = putByte,
475
    bytesToInts = bytesToInts,
476
    intsToBytes = intsToBytes,
477
    bytesToHex = bytesToHex,
478
    hexToBytes = hexToBytes,
479
    toHexString = toHexString,
480
    padByteString = padByteString,
481
    properlyDecrypted = properlyDecrypted,
482
    unpadByteString = unpadByteString,
483
    xorIV = xorIV,
484
    increment = increment,
485
    sleepCheckIn = sleepCheckIn,
486
    getRandomData = getRandomData,
487
    getRandomString = getRandomString,
488
  }
489
  end)
490
491
  aes=_W(function(_ENV, ...)
492
  -- Implementation of AES with nearly pure lua
493
  -- AES with lua is slow, really slow :-)
494
  local putByte = util.putByte
495
  local getByte = util.getByte
496
  -- some constants
497
  local ROUNDS = 'rounds'
498
  local KEY_TYPE = "type"
499
  local ENCRYPTION_KEY=1
500
  local DECRYPTION_KEY=2
501
  -- aes SBOX
502
  local SBox = {}
503
  local iSBox = {}
504
  -- aes tables
505
  local table0 = {}
506
  local table1 = {}
507
  local table2 = {}
508
  local table3 = {}
509
  local tableInv0 = {}
510
  local tableInv1 = {}
511
  local tableInv2 = {}
512
  local tableInv3 = {}
513
  -- round constants
514
  local rCon = {
515
    0x01000000,
516
    0x02000000,
517
    0x04000000,
518
    0x08000000,
519
    0x10000000,
520
    0x20000000,
521
    0x40000000,
522
    0x80000000,
523
    0x1b000000,
524
    0x36000000,
525
    0x6c000000,
526
    0xd8000000,
527
    0xab000000,
528
    0x4d000000,
529
    0x9a000000,
530
    0x2f000000,
531
  }
532
  --
533
  -- affine transformation for calculating the S-Box of AES
534
  --
535
  local function affinMap(byte)
536
    mask = 0xf8
537
    result = 0
538
    for i = 1,8 do
539
      result = bit.lshift(result,1)
540
      parity = util.byteParity(bit.band(byte,mask))
541
      result = result + parity
542
      -- simulate roll
543
      lastbit = bit.band(mask, 1)
544
      mask = bit.band(bit.rshift(mask, 1),0xff)
545
      mask = lastbit ~= 0 and bit.bor(mask, 0x80) or bit.band(mask, 0x7f) 
546
    end
547
    return bit.bxor(result, 0x63)
548
  end
549
  --
550
  -- calculate S-Box and inverse S-Box of AES
551
  -- apply affine transformation to inverse in finite field 2^8
552
  --
553
  local function calcSBox()
554
    for i = 0, 255 do
555
      inverse = i ~= 0 and gf.invert(i) or i
556
      mapped = affinMap(inverse)
557
      SBox[i] = mapped
558
      iSBox[mapped] = i
559
    end
560
  end
561
  --
562
  -- Calculate round tables
563
  -- round tables are used to calculate shiftRow, MixColumn and SubBytes
564
  -- with 4 table lookups and 4 xor operations.
565
  --
566
  local function calcRoundTables()
567
    for x = 0,255 do
568
      byte = SBox[x]
569
      table0[x] = putByte(gf.mul(0x03, byte), 0)
570
                + putByte(             byte , 1)
571
                + putByte(             byte , 2)
572
                + putByte(gf.mul(0x02, byte), 3)
573
      table1[x] = putByte(             byte , 0)
574
                + putByte(             byte , 1)
575
                + putByte(gf.mul(0x02, byte), 2)
576
                + putByte(gf.mul(0x03, byte), 3)
577
      table2[x] = putByte(             byte , 0)
578
                + putByte(gf.mul(0x02, byte), 1)
579
                + putByte(gf.mul(0x03, byte), 2)
580
                + putByte(             byte , 3)
581
      table3[x] = putByte(gf.mul(0x02, byte), 0)
582
                + putByte(gf.mul(0x03, byte), 1)
583
                + putByte(             byte , 2)
584
                + putByte(             byte , 3)
585
    end
586
  end
587
  --
588
  -- Calculate inverse round tables
589
  -- does the inverse of the normal roundtables for the equivalent
590
  -- decryption algorithm.
591
  --
592
  local function calcInvRoundTables()
593
    for x = 0,255 do
594
      byte = iSBox[x]
595
      tableInv0[x] = putByte(gf.mul(0x0b, byte), 0)
596
                 + putByte(gf.mul(0x0d, byte), 1)
597
                 + putByte(gf.mul(0x09, byte), 2)
598
                 + putByte(gf.mul(0x0e, byte), 3)
599
      tableInv1[x] = putByte(gf.mul(0x0d, byte), 0)
600
                 + putByte(gf.mul(0x09, byte), 1)
601
                 + putByte(gf.mul(0x0e, byte), 2)
602
                 + putByte(gf.mul(0x0b, byte), 3)
603
      tableInv2[x] = putByte(gf.mul(0x09, byte), 0)
604
                 + putByte(gf.mul(0x0e, byte), 1)
605
                 + putByte(gf.mul(0x0b, byte), 2)
606
                 + putByte(gf.mul(0x0d, byte), 3)
607
      tableInv3[x] = putByte(gf.mul(0x0e, byte), 0)
608
                 + putByte(gf.mul(0x0b, byte), 1)
609
                 + putByte(gf.mul(0x0d, byte), 2)
610
                 + putByte(gf.mul(0x09, byte), 3)
611
    end
612
  end
613
  --
614
  -- rotate word: 0xaabbccdd gets 0xbbccddaa
615
  -- used for key schedule
616
  --
617
  local function rotWord(word)
618
    local tmp = bit.band(word,0xff000000)
619
    return (bit.lshift(word,8) + bit.rshift(tmp,24))
620
  end
621
  --
622
  -- replace all bytes in a word with the SBox.
623
  -- used for key schedule
624
  --
625
  local function subWord(word)
626
    return putByte(SBox[getByte(word,0)],0)
627
      + putByte(SBox[getByte(word,1)],1)
628
      + putByte(SBox[getByte(word,2)],2)
629
      + putByte(SBox[getByte(word,3)],3)
630
  end
631
  --
632
  -- generate key schedule for aes encryption
633
  --
634
  -- returns table with all round keys and
635
  -- the necessary number of rounds saved in [ROUNDS]
636
  --
637
  local function expandEncryptionKey(key)
638
    local keySchedule = {}
639
    local keyWords = math.floor(#key / 4)
640
    if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then
641
      error("Invalid key size: " .. tostring(keyWords))
642
      return nil
643
    end
644
    keySchedule[ROUNDS] = keyWords + 6
645
    keySchedule[KEY_TYPE] = ENCRYPTION_KEY
646
    for i = 0,keyWords - 1 do
647
      keySchedule[i] = putByte(key[i*4+1], 3)
648
               + putByte(key[i*4+2], 2)
649
               + putByte(key[i*4+3], 1)
650
               + putByte(key[i*4+4], 0)
651
    end
652
    for i = keyWords, (keySchedule[ROUNDS] + 1)*4 - 1 do
653
      local tmp = keySchedule[i-1]
654
      if ( i % keyWords == 0) then
655
        tmp = rotWord(tmp)
656
        tmp = subWord(tmp)
657
        local index = math.floor(i/keyWords)
658
        tmp = bit.bxor(tmp,rCon[index])
659
      elseif (keyWords > 6 and i % keyWords == 4) then
660
        tmp = subWord(tmp)
661
      end
662
      keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp)
663
    end
664
    return keySchedule
665
  end
666
  --
667
  -- Inverse mix column
668
  -- used for key schedule of decryption key
669
  --
670
  local function invMixColumnOld(word)
671
    local b0 = getByte(word,3)
672
    local b1 = getByte(word,2)
673
    local b2 = getByte(word,1)
674
    local b3 = getByte(word,0)
675
    return putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1),
676
                         gf.mul(0x0d, b2)),
677
                         gf.mul(0x09, b3)),
678
                         gf.mul(0x0e, b0)),3)
679
       + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2),
680
                         gf.mul(0x0d, b3)),
681
                         gf.mul(0x09, b0)),
682
                         gf.mul(0x0e, b1)),2)
683
       + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3),
684
                         gf.mul(0x0d, b0)),
685
                         gf.mul(0x09, b1)),
686
                         gf.mul(0x0e, b2)),1)
687
       + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0),
688
                         gf.mul(0x0d, b1)),
689
                         gf.mul(0x09, b2)),
690
                         gf.mul(0x0e, b3)),0)
691
  end
692
  --
693
  -- Optimized inverse mix column
694
  -- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
695
  -- TODO: make it work
696
  --
697
  local function invMixColumn(word)
698
    local b0 = getByte(word,3)
699
    local b1 = getByte(word,2)
700
    local b2 = getByte(word,1)
701
    local b3 = getByte(word,0)
702
    local t = bit.bxor(b3,b2)
703
    local u = bit.bxor(b1,b0)
704
    local v = bit.bxor(t,u)
705
    v = bit.bxor(v,gf.mul(0x08,v))
706
    w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0)))
707
    v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1)))
708
    return putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0)
709
       + putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t              )), 1)
710
       + putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2)
711
       + putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u              )), 3)
712
  end
713
  --
714
  -- generate key schedule for aes decryption
715
  --
716
  -- uses key schedule for aes encryption and transforms each
717
  -- key by inverse mix column.
718
  --
719
  local function expandDecryptionKey(key)
720
    local keySchedule = expandEncryptionKey(key)
721
    if (keySchedule == nil) then
722
      return nil
723
    end
724
    keySchedule[KEY_TYPE] = DECRYPTION_KEY
725
    for i = 4, (keySchedule[ROUNDS] + 1)*4 - 5 do
726
      keySchedule[i] = invMixColumnOld(keySchedule[i])
727
    end
728
    return keySchedule
729
  end
730
  --
731
  -- xor round key to state
732
  --
733
  local function addRoundKey(state, key, round)
734
    for i = 0, 3 do
735
      state[i + 1] = bit.bxor(state[i + 1], key[round*4+i])
736
    end
737
  end
738
  --
739
  -- do encryption round (ShiftRow, SubBytes, MixColumn together)
740
  --
741
  local function doRound(origState, dstState)
742
    dstState[1] =  bit.bxor(bit.bxor(bit.bxor(
743
          table0[getByte(origState[1],3)],
744
          table1[getByte(origState[2],2)]),
745
          table2[getByte(origState[3],1)]),
746
          table3[getByte(origState[4],0)])
747
    dstState[2] =  bit.bxor(bit.bxor(bit.bxor(
748
          table0[getByte(origState[2],3)],
749
          table1[getByte(origState[3],2)]),
750
          table2[getByte(origState[4],1)]),
751
          table3[getByte(origState[1],0)])
752
    dstState[3] =  bit.bxor(bit.bxor(bit.bxor(
753
          table0[getByte(origState[3],3)],
754
          table1[getByte(origState[4],2)]),
755
          table2[getByte(origState[1],1)]),
756
          table3[getByte(origState[2],0)])
757
    dstState[4] =  bit.bxor(bit.bxor(bit.bxor(
758
          table0[getByte(origState[4],3)],
759
          table1[getByte(origState[1],2)]),
760
          table2[getByte(origState[2],1)]),
761
          table3[getByte(origState[3],0)])
762
  end
763
  --
764
  -- do last encryption round (ShiftRow and SubBytes)
765
  --
766
  local function doLastRound(origState, dstState)
767
    dstState[1] = putByte(SBox[getByte(origState[1],3)], 3)
768
          + putByte(SBox[getByte(origState[2],2)], 2)
769
          + putByte(SBox[getByte(origState[3],1)], 1)
770
          + putByte(SBox[getByte(origState[4],0)], 0)
771
    dstState[2] = putByte(SBox[getByte(origState[2],3)], 3)
772
          + putByte(SBox[getByte(origState[3],2)], 2)
773
          + putByte(SBox[getByte(origState[4],1)], 1)
774
          + putByte(SBox[getByte(origState[1],0)], 0)
775
    dstState[3] = putByte(SBox[getByte(origState[3],3)], 3)
776
          + putByte(SBox[getByte(origState[4],2)], 2)
777
          + putByte(SBox[getByte(origState[1],1)], 1)
778
          + putByte(SBox[getByte(origState[2],0)], 0)
779
    dstState[4] = putByte(SBox[getByte(origState[4],3)], 3)
780
          + putByte(SBox[getByte(origState[1],2)], 2)
781
          + putByte(SBox[getByte(origState[2],1)], 1)
782
          + putByte(SBox[getByte(origState[3],0)], 0)
783
  end
784
  --
785
  -- do decryption round
786
  --
787
  local function doInvRound(origState, dstState)
788
    dstState[1] =  bit.bxor(bit.bxor(bit.bxor(
789
          tableInv0[getByte(origState[1],3)],
790
          tableInv1[getByte(origState[4],2)]),
791
          tableInv2[getByte(origState[3],1)]),
792
          tableInv3[getByte(origState[2],0)])
793
    dstState[2] =  bit.bxor(bit.bxor(bit.bxor(
794
          tableInv0[getByte(origState[2],3)],
795
          tableInv1[getByte(origState[1],2)]),
796
          tableInv2[getByte(origState[4],1)]),
797
          tableInv3[getByte(origState[3],0)])
798
    dstState[3] =  bit.bxor(bit.bxor(bit.bxor(
799
          tableInv0[getByte(origState[3],3)],
800
          tableInv1[getByte(origState[2],2)]),
801
          tableInv2[getByte(origState[1],1)]),
802
          tableInv3[getByte(origState[4],0)])
803
    dstState[4] =  bit.bxor(bit.bxor(bit.bxor(
804
          tableInv0[getByte(origState[4],3)],
805
          tableInv1[getByte(origState[3],2)]),
806
          tableInv2[getByte(origState[2],1)]),
807
          tableInv3[getByte(origState[1],0)])
808
  end
809
  --
810
  -- do last decryption round
811
  --
812
  local function doInvLastRound(origState, dstState)
813
    dstState[1] = putByte(iSBox[getByte(origState[1],3)], 3)
814
          + putByte(iSBox[getByte(origState[4],2)], 2)
815
          + putByte(iSBox[getByte(origState[3],1)], 1)
816
          + putByte(iSBox[getByte(origState[2],0)], 0)
817
    dstState[2] = putByte(iSBox[getByte(origState[2],3)], 3)
818
          + putByte(iSBox[getByte(origState[1],2)], 2)
819
          + putByte(iSBox[getByte(origState[4],1)], 1)
820
          + putByte(iSBox[getByte(origState[3],0)], 0)
821
    dstState[3] = putByte(iSBox[getByte(origState[3],3)], 3)
822
          + putByte(iSBox[getByte(origState[2],2)], 2)
823
          + putByte(iSBox[getByte(origState[1],1)], 1)
824
          + putByte(iSBox[getByte(origState[4],0)], 0)
825
    dstState[4] = putByte(iSBox[getByte(origState[4],3)], 3)
826
          + putByte(iSBox[getByte(origState[3],2)], 2)
827
          + putByte(iSBox[getByte(origState[2],1)], 1)
828
          + putByte(iSBox[getByte(origState[1],0)], 0)
829
  end
830
  --
831
  -- encrypts 16 Bytes
832
  -- key           encryption key schedule
833
  -- input         array with input data
834
  -- inputOffset   start index for input
835
  -- output        array for encrypted data
836
  -- outputOffset  start index for output
837
  --
838
  local function encrypt(key, input, inputOffset, output, outputOffset)
839
    --default parameters
840
    inputOffset = inputOffset or 1
841
    output = output or {}
842
    outputOffset = outputOffset or 1
843
    local state = {}
844
    local tmpState = {}
845
    if (key[KEY_TYPE] ~= ENCRYPTION_KEY) then
846
      error("No encryption key: " .. tostring(key[KEY_TYPE]) .. ", expected " .. ENCRYPTION_KEY)
847
      return
848
    end
849
    state = util.bytesToInts(input, inputOffset, 4)
850
    addRoundKey(state, key, 0)
851
    local round = 1
852
    while (round < key[ROUNDS] - 1) do
853
      -- do a double round to save temporary assignments
854
      doRound(state, tmpState)
855
      addRoundKey(tmpState, key, round)
856
      round = round + 1
857
      doRound(tmpState, state)
858
      addRoundKey(state, key, round)
859
      round = round + 1
860
    end
861
    doRound(state, tmpState)
862
    addRoundKey(tmpState, key, round)
863
    round = round +1
864
    doLastRound(tmpState, state)
865
    addRoundKey(state, key, round)
866
    util.sleepCheckIn()
867
    return util.intsToBytes(state, output, outputOffset)
868
  end
869
  --
870
  -- decrypt 16 bytes
871
  -- key           decryption key schedule
872
  -- input         array with input data
873
  -- inputOffset   start index for input
874
  -- output        array for decrypted data
875
  -- outputOffset  start index for output
876
  ---
877
  local function decrypt(key, input, inputOffset, output, outputOffset)
878
    -- default arguments
879
    inputOffset = inputOffset or 1
880
    output = output or {}
881
    outputOffset = outputOffset or 1
882
    local state = {}
883
    local tmpState = {}
884
    if (key[KEY_TYPE] ~= DECRYPTION_KEY) then
885
      error("No decryption key: " .. tostring(key[KEY_TYPE]))
886
      return
887
    end
888
    state = util.bytesToInts(input, inputOffset, 4)
889
    addRoundKey(state, key, key[ROUNDS])
890
    local round = key[ROUNDS] - 1
891
    while (round > 2) do
892
      -- do a double round to save temporary assignments
893
      doInvRound(state, tmpState)
894
      addRoundKey(tmpState, key, round)
895
      round = round - 1
896
      doInvRound(tmpState, state)
897
      addRoundKey(state, key, round)
898
      round = round - 1
899
    end
900
    doInvRound(state, tmpState)
901
    addRoundKey(tmpState, key, round)
902
    round = round - 1
903
    doInvLastRound(tmpState, state)
904
    addRoundKey(state, key, round)
905
    util.sleepCheckIn()
906
    return util.intsToBytes(state, output, outputOffset)
907
  end
908
909
  -- calculate all tables when loading this file
910
  calcSBox()
911
  calcRoundTables()
912
  calcInvRoundTables()
913
914
  return {
915
    ROUNDS = ROUNDS,
916
    KEY_TYPE = KEY_TYPE,
917
    ENCRYPTION_KEY = ENCRYPTION_KEY,
918
    DECRYPTION_KEY = DECRYPTION_KEY,
919
    expandEncryptionKey = expandEncryptionKey,
920
    expandDecryptionKey = expandDecryptionKey,
921
    encrypt = encrypt,
922
    decrypt = decrypt,
923
  }
924
  end)
925
926
  local buffer=_W(function(_ENV, ...)
927
  local function new ()
928
    return {}
929
  end
930
931
  local function addString (stack, s)
932
    table.insert(stack, s)
933
  end
934
935
  local function toString (stack)
936
    return table.concat(stack)
937
  end
938
939
  return {
940
    new = new,
941
    addString = addString,
942
    toString = toString,
943
  }
944
  end)
945
946
  ciphermode=_W(function(_ENV, ...)
947
  local public = {}
948
  --
949
  -- Encrypt strings
950
  -- key - byte array with key
951
  -- string - string to encrypt
952
  -- modefunction - function for cipher mode to use
953
  --
954
  local random, unpack = math.random, unpack or table.unpack
955
  function public.encryptString(key, data, modeFunction, iv)
956
    if iv then
957
      local ivCopy = {}
958
      for i = 1, 16 do ivCopy[i] = iv[i] end
959
      iv = ivCopy
960
    else
961
      iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
962
    end
963
    local keySched = aes.expandEncryptionKey(key)
964
    local encryptedData = buffer.new()
965
    for i = 1, #data/16 do
966
      local offset = (i-1)*16 + 1
967
      local byteData = {string.byte(data,offset,offset +15)}
968
      iv = modeFunction(keySched, byteData, iv)
969
      buffer.addString(encryptedData, string.char(unpack(byteData)))
970
    end
971
    return buffer.toString(encryptedData)
972
  end
973
  --
974
  -- the following 4 functions can be used as
975
  -- modefunction for encryptString
976
  --
977
  -- Electronic code book mode encrypt function
978
  function public.encryptECB(keySched, byteData, iv)
979
    aes.encrypt(keySched, byteData, 1, byteData, 1)
980
  end
981
982
  -- Cipher block chaining mode encrypt function
983
  function public.encryptCBC(keySched, byteData, iv)
984
    util.xorIV(byteData, iv)
985
    aes.encrypt(keySched, byteData, 1, byteData, 1)
986
    return byteData
987
  end
988
989
  -- Output feedback mode encrypt function
990
  function public.encryptOFB(keySched, byteData, iv)
991
    aes.encrypt(keySched, iv, 1, iv, 1)
992
    util.xorIV(byteData, iv)
993
    return iv
994
  end
995
996
  -- Cipher feedback mode encrypt function
997
  function public.encryptCFB(keySched, byteData, iv)
998
    aes.encrypt(keySched, iv, 1, iv, 1)
999
    util.xorIV(byteData, iv)
1000
    return byteData
1001
  end
1002
1003
  function public.encryptCTR(keySched, byteData, iv)
1004
    local nextIV = {}
1005
    for j = 1, 16 do nextIV[j] = iv[j] end
1006
    aes.encrypt(keySched, iv, 1, iv, 1)
1007
    util.xorIV(byteData, iv)
1008
    util.increment(nextIV)
1009
    return nextIV
1010
  end
1011
  --
1012
  -- Decrypt strings
1013
  -- key - byte array with key
1014
  -- string - string to decrypt
1015
  -- modefunction - function for cipher mode to use
1016
  --
1017
  function public.decryptString(key, data, modeFunction, iv)
1018
    if iv then
1019
      local ivCopy = {}
1020
      for i = 1, 16 do ivCopy[i] = iv[i] end
1021
      iv = ivCopy
1022
    else
1023
      iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
1024
    end
1025
    local keySched
1026
    if modeFunction == public.decryptOFB or modeFunction == public.decryptCFB or modeFunction == public.decryptCTR then
1027
      keySched = aes.expandEncryptionKey(key)
1028
    else
1029
      keySched = aes.expandDecryptionKey(key)
1030
    end
1031
    local decryptedData = buffer.new()
1032
    for i = 1, #data/16 do
1033
      local offset = (i-1)*16 + 1
1034
      local byteData = {string.byte(data,offset,offset +15)}
1035
      iv = modeFunction(keySched, byteData, iv)
1036
      buffer.addString(decryptedData, string.char(unpack(byteData)))
1037
    end
1038
    return buffer.toString(decryptedData)
1039
  end
1040
  --
1041
  -- the following 4 functions can be used as
1042
  -- modefunction for decryptString
1043
  --
1044
  -- Electronic code book mode decrypt function
1045
  function public.decryptECB(keySched, byteData, iv)
1046
    aes.decrypt(keySched, byteData, 1, byteData, 1)
1047
    return iv
1048
  end
1049
1050
  -- Cipher block chaining mode decrypt function
1051
  function public.decryptCBC(keySched, byteData, iv)
1052
    local nextIV = {}
1053
    for j = 1, 16 do nextIV[j] = byteData[j] end
1054
    aes.decrypt(keySched, byteData, 1, byteData, 1)
1055
    util.xorIV(byteData, iv)
1056
    return nextIV
1057
  end
1058
1059
  -- Output feedback mode decrypt function
1060
  function public.decryptOFB(keySched, byteData, iv)
1061
    aes.encrypt(keySched, iv, 1, iv, 1)
1062
    util.xorIV(byteData, iv)
1063
    return iv
1064
  end
1065
1066
  -- Cipher feedback mode decrypt function
1067
  function public.decryptCFB(keySched, byteData, iv)
1068
    local nextIV = {}
1069
    for j = 1, 16 do nextIV[j] = byteData[j] end
1070
    aes.encrypt(keySched, iv, 1, iv, 1)
1071
    util.xorIV(byteData, iv)
1072
    return nextIV
1073
  end
1074
1075
  public.decryptCTR = public.encryptCTR
1076
  return public
1077
  end)
1078
1079
  -- Simple API for encrypting strings.
1080
  --
1081
  AES128 = 16
1082
  AES192 = 24
1083
  AES256 = 32
1084
  ECBMODE = 1
1085
  CBCMODE = 2
1086
  OFBMODE = 3
1087
  CFBMODE = 4
1088
  CTRMODE = 4
1089
1090
  local function pwToKey(password, keyLength, iv)
1091
    local padLength = keyLength
1092
    if (keyLength == AES192) then
1093
      padLength = 32
1094
    end
1095
    if (padLength > #password) then
1096
      local postfix = ""
1097
      for i = 1,padLength - #password do
1098
        postfix = postfix .. string.char(0)
1099
      end
1100
      password = password .. postfix
1101
    else
1102
      password = string.sub(password, 1, padLength)
1103
    end
1104
    local pwBytes = {string.byte(password,1,#password)}
1105
    password = ciphermode.encryptString(pwBytes, password, ciphermode.encryptCBC, iv)
1106
    password = string.sub(password, 1, keyLength)
1107
    return {string.byte(password,1,#password)}
1108
  end
1109
  --
1110
  -- Encrypts string data with password password.
1111
  -- password  - the encryption key is generated from this string
1112
  -- data      - string to encrypt (must not be too large)
1113
  -- keyLength - length of aes key: 128(default), 192 or 256 Bit
1114
  -- mode      - mode of encryption: ecb, cbc(default), ofb, cfb
1115
  --
1116
  -- mode and keyLength must be the same for encryption and decryption.
1117
  --
1118
  function encrypt(password, data, keyLength, mode, iv)
1119
    assert(password ~= nil, "Empty password.")
1120
    assert(data ~= nil, "Empty data.")
1121
    local mode = mode or CBCMODE
1122
    local keyLength = keyLength or AES128
1123
    local key = pwToKey(password, keyLength, iv)
1124
    local paddedData = util.padByteString(data)
1125
    if mode == ECBMODE then
1126
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptECB, iv)
1127
    elseif mode == CBCMODE then
1128
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptCBC, iv)
1129
    elseif mode == OFBMODE then
1130
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptOFB, iv)
1131
    elseif mode == CFBMODE then
1132
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptCFB, iv)
1133
    elseif mode == CTRMODE then
1134
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptCTR, iv)
1135
    else
1136
      error("Unknown mode", 2)
1137
    end
1138
  end
1139
  --
1140
  -- Decrypts string data with password password.
1141
  -- password  - the decryption key is generated from this string
1142
  -- data      - string to encrypt
1143
  -- keyLength - length of aes key: 128(default), 192 or 256 Bit
1144
  -- mode      - mode of decryption: ecb, cbc(default), ofb, cfb
1145
  --
1146
  -- mode and keyLength must be the same for encryption and decryption.
1147
  --
1148
  function decrypt(password, data, keyLength, mode, iv)
1149
    local mode = mode or CBCMODE
1150
    local keyLength = keyLength or AES128
1151
    local key = pwToKey(password, keyLength, iv)
1152
    local plain
1153
    if mode == ECBMODE then
1154
      plain = ciphermode.decryptString(key, data, ciphermode.decryptECB, iv)
1155
    elseif mode == CBCMODE then
1156
      plain = ciphermode.decryptString(key, data, ciphermode.decryptCBC, iv)
1157
    elseif mode == OFBMODE then
1158
      plain = ciphermode.decryptString(key, data, ciphermode.decryptOFB, iv)
1159
    elseif mode == CFBMODE then
1160
      plain = ciphermode.decryptString(key, data, ciphermode.decryptCFB, iv)
1161
    elseif mode == CTRMODE then
1162
      plain = ciphermode.decryptString(key, data, ciphermode.decryptCTR, iv)
1163
    else
1164
      error("Unknown mode", 2)
1165
    end
1166
    result = util.unpadByteString(plain)
1167
    if (result == nil) then
1168
      return nil
1169
    end
1170
    return result
1171
  end
1172
end
1173
1174
local function saveData()
1175
  local clientConfig = fs.open(config, "w") or error("saveData(): Cannot open " .. config .. " for writing", 0)
1176
  clientConfig.write(textutils.serialize(ccSettings))
1177
  clientConfig.close()
1178
end
1179
1180
local function doAction()
1181
  if (thisCommand == "ON"  or thisCommand == "CLOSED") and not ccSettings.lockState then
1182
    rs.setOutput(ccSettings.side, onState)
1183
    deviceState = thisCommand
1184
    ccSettings.lastState = onState
1185
    saveData()
1186
    return true
1187
  elseif (thisCommand == "OFF" or thisCommand == "OPEN") and not ccSettings.lockState then
1188
    rs.setOutput(ccSettings.side, offState)
1189
    deviceState = thisCommand
1190
    ccSettings.lastState = offState
1191
    if autoClose then closeTimer = os.startTimer(autoDelay) end
1192
    saveData()
1193
    return true
1194
  elseif thisCommand == "LOCKED" or thisCommand == "UNLOCK" then
1195
    ccSettings.lockState = thisCommand == "LOCKED"
1196
    if not ccSettings.lockState and (deviceState == "OPEN" or deviceState == "OFF") and autoClose then
1197
      closeTimer = os.startTimer(autoDelay)
1198
    end
1199
    saveData()
1200
    return true
1201
  elseif thisCommand == "WiReQRY" then
1202
    local rsOut = rs.getOutput(ccSettings.side)
1203
    if deviceType == "Door" then
1204
      deviceState = rsOut and "OPEN" or "CLOSED"
1205
    elseif deviceType == "Piston" or deviceType == "eDoor" then
1206
      deviceState = rsOut and "CLOSED" or "OPEN"
1207
    elseif deviceType == "Create" then
1208
      deviceState = rsOut and "OFF" or "ON"
1209
    else
1210
      deviceState = rsOut and "ON" or "OFF"
1211
    end
1212
    return true
1213
  end
1214
  return false
1215
end
1216
1217
local function netSend()
1218
  local dataPack = textutils.serialize({
1219
    program = "WiRe";
1220
    cc = tonumber(thisCC);
1221
    name = ccSettings.name;
1222
    color = ccSettings.color;
1223
    deviceType = deviceType;
1224
    deviceState = deviceState;
1225
    lockState = ccSettings.lockState;
1226
    quietCount = 0;
1227
    loc = loc;
1228
  })
1229
  if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
1230
  local encKey = tostring(server) .. "WiRe!Comms" .. thisCC
1231
  local encryptedPackage = encode(encrypt(encKey, dataPack))
1232
  rednet.send(server, encryptedPackage, network)
1233
end
1234
1235
local function netReceive()
1236
  local id, newCmdData, goodData, encryptedMessage, decryptedMessage, decodedMessage, encKey, success
1237
  while true do
1238
    if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
1239
    newCmdData = { }
1240
    id, encryptedMessage = rednet.receive(network)
1241
    goodData = false
1242
    if type(encryptedMessage) == "string" then
1243
      success, decodedMessage = pcall(decode, encryptedMessage)
1244
      if success and type(decodedMessage) == "string" then
1245
        encKey = thisCC .. "WiRe!Comms" .. tostring(id)
1246
        success, decryptedMessage = pcall(decrypt, encKey, decodedMessage)
1247
        if success and type(decryptedMessage) == "string" then
1248
          success, newCmdData = pcall(textutils.unserialize, decryptedMessage)
1249
          if success and type(newCmdData) == "table" and newCmdData.program then
1250
            if newCmdData.program == "WiRe" and newCmdData.cc == id and id == server and newCmdData.color == ccSettings.color then
1251
              if validStates[newCmdData.cmd] then
1252
                thisCommand = newCmdData.cmd
1253
                goodData = true
1254
              end
1255
            end
1256
          end
1257
        else
1258
          encKey = tostring(id) .. "WiRe!Comms" .. tostring(id)
1259
          success, decryptedMessage = pcall(decrypt, encKey, decodedMessage)
1260
          if success and type(decryptedMessage) == "string" then
1261
            success, newCmdData = pcall(textutils.unserialize, decryptedMessage)
1262
            if success and type(newCmdData) == "table" and newCmdData.program then
1263
              if newCmdData.program == "WiRe" and newCmdData.cc == id and id == server and newCmdData.color == ccSettings.color then
1264
                if validStates[newCmdData.cmd] then
1265
                  thisCommand = newCmdData.cmd
1266
                  goodData = true
1267
                end
1268
              end
1269
            end
1270
          end
1271
        end
1272
      end
1273
    end
1274
    if goodData then
1275
      ccSuccess = doAction()
1276
      netSend()
1277
    else
1278
      thisCommand = "Noise"
1279
      ccSuccess = false
1280
    end
1281
    updateScreens()
1282
  end
1283
end
1284
1285
do
1286
  local colorBurst = {
1287
    Purple = purple;
1288
    Magenta = magenta;
1289
    Blue = blue;
1290
    Sky = sky;
1291
    Cyan = cyan;
1292
    Green = green;
1293
    Lime = lime;
1294
    Red = red;
1295
    Orange = orange;
1296
    Yellow = yellow;
1297
    Brown = brown;
1298
    Silver = silver;
1299
    Gray = gray;
1300
    White = white;
1301
    Black = white;
1302
  }
1303
1304
  local labels = {
1305
    [1] = { "Name", 1, 3 };
1306
    [2] = { "Note:", 1, 4 };
1307
    [3] = { "Group:", 1, 6 };
1308
    [4] = { "cc#", 1, 8 };
1309
    [5] = { "Server cc#", 1, 9 };
1310
    [6] = { "Modem:", 1, 11 };
1311
    [7] = { "Redstone:", 1, 12 };
1312
    [8] = { "Device Type:", 1, 14 };
1313
    [9] = { "Current State:", 1, 15 };
1314
    [10] = { "Last Command:", 1, 17 };
1315
    [11] = { "Success:", 1, 18 };
1316
    [12] = { "Peripherals", 28, 3 };
1317
    [13] = { "Location: x:", 28, 11 };
1318
    [14] = { "y:", 38, 12 };
1319
    [15] = { "z:", 38, 13 };
1320
    [16] = { "Check-in:", 31, 17 };
1321
  }
1322
1323
  staticTermScreen = function()
1324
    local hText = "WiRe Client " .. WiReCver
1325
    local spacer = (termX - #hText) / 2
1326
    term.setBackgroundColor(blue)
1327
    term.setTextColor(white)
1328
    term.setCursorPos(1, 1)
1329
    term.write(string.rep(" ", math.floor(spacer)) .. hText .. string.rep(" ", math.ceil(spacer)))
1330
    term.setBackgroundColor(black)
1331
    term.setTextColor(silver)
1332
    for i = 1, 16 do
1333
      term.setCursorPos(labels[i][2], labels[i][3])
1334
      term.write(labels[i][1])
1335
    end
1336
    term.setTextColor(white)
1337
    term.setCursorPos(7, 3)
1338
    term.write(ccSettings.name)
1339
    term.setCursorPos(7, 4)
1340
    term.write(ccSettings.note)
1341
    term.setCursorPos(5, 8)
1342
    term.write(thisCC)
1343
    term.setCursorPos(12, 9)
1344
    term.write(tostring(server))
1345
    term.setCursorPos(11, 11)
1346
    term.write(modemSide)
1347
    term.setCursorPos(11, 12)
1348
    term.write(ccSettings.side)
1349
    term.setCursorPos(16, 14)
1350
    term.write(deviceType)
1351
    if mon[1] then
1352
      term.setCursorPos(28, 5)
1353
      term.write("Monitor x" .. tostring(#mon))
1354
    end
1355
    term.setCursorPos(41, 11)
1356
    term.write(tostring(loc.x))
1357
    term.setCursorPos(41, 12)
1358
    term.write(tostring(loc.y))
1359
    term.setCursorPos(41, 13)
1360
    term.write(tostring(loc.z))
1361
    term.setTextColor(colorBurst[ccSettings.color] or silver)
1362
    term.setCursorPos(8, 6)
1363
    term.write(ccSettings.color)
1364
  end
1365
end
1366
1367
local function termScreen()
1368
  term.setCursorPos(16, 15)
1369
  term.setTextColor(switchStates[deviceState][1] or yellow)
1370
  term.write(deviceState)
1371
  term.setTextColor(gray)
1372
  term.write(" | ")
1373
  term.setTextColor(ccSettings.lockState and red or green)
1374
  term.write(ccSettings.lockState and "Locked       " or "Unlocked       ")
1375
  term.setCursorPos(15, 17)
1376
  term.setTextColor(white)
1377
  term.write(thisCommand .. string.rep(" ", 8))
1378
  term.setCursorPos(15, 18)
1379
  term.setTextColor(ccSuccess and green or red)
1380
  term.write(tostring(ccSuccess) .. " ")
1381
end
1382
1383
local function helpScreen()
1384
  local hText = "WiRe Client Help"
1385
  local xPos, spacer = math.floor(termX / 2), (termX - #hText) / 2
1386
  term.setBackgroundColor(white)
1387
  term.clear()
1388
  term.setBackgroundColor(blue)
1389
  term.setTextColor(white)
1390
  term.setCursorPos(1, 1)
1391
  term.write(string.rep(" ", math.floor(spacer)) .. hText .. string.rep(" ", math.ceil(spacer)))
1392
  term.setBackgroundColor(white)
1393
  term.setTextColor(black)
1394
  term.setCursorPos(xPos - 8, 5)
1395
  term.write("'q' to quit client")
1396
  term.setCursorPos(xPos - 12, 7)
1397
  term.write("'F1' to display/exit help")
1398
end
1399
1400
do
1401
  local function touchScreen()
1402
    local line, symbol = "     "
1403
    for i = 1, #mon do
1404
      symbol = ccSettings.lockState and " [0] " or (switchStates[deviceState][3] or " [X] ")
1405
      mon[i].setBackgroundColor(ccSettings.lockState and mred or (switchStates[deviceState][2] or myellow))
1406
      mon[i].setTextColor(mwhite)
1407
      for y = 2, 4 do
1408
        mon[i].setCursorPos(2, y)
1409
        mon[i].write(y == 3 and symbol or line)
1410
      end
1411
    end
1412
  end
1413
1414
  updateScreens = function()
1415
    if not tArgs[1] and not help then termScreen() end
1416
    if thisCommand ~= "Noise" and mon[1] then touchScreen() end
1417
  end
1418
end
1419
1420
local function clearMonitors()
1421
  for i = 1, #mon do
1422
    mon[i].setBackgroundColor(mblack)
1423
    mon[i].clear()
1424
  end
1425
end
1426
1427
local function clearTerm()
1428
  term.setBackgroundColor(black)
1429
  term.setTextColor(white)
1430
  term.clear()
1431
  term.setCursorPos(1, 1)
1432
end
1433
1434
local function shutDown()
1435
  deviceState = "OFFLINE"
1436
  kernelState = false
1437
  netSend()
1438
  if rednet.isOpen(modemSide) then rednet.close(modemSide) end
1439
  if mon[1] then clearMonitors() end
1440
end
1441
1442
local function foregroundShell()
1443
  clearTerm()
1444
  if fs.exists(tArgs[1]) then
1445
    local unpack = unpack or table.unpack
1446
	  shell.run(unpack(tArgs))
1447
    clearTerm()
1448
  else
1449
    term.write(tArgs[1] .. " missing")
1450
    term.setCursorPos(1, 3)
1451
  end
1452
  shutDown()
1453
  term.write("WiRe Client is OFFLINE")
1454
  term.setCursorPos(1, 5)
1455
end
1456
1457
local function dataPoller()
1458
  local _, timer, thisTime
1459
  while true do
1460
    _, timer = os.pullEvent("timer")
1461
    thisCommand = "Noise"
1462
    ccSuccess = false
1463
    if timer == pollTimer then
1464
      if kernelState then
1465
        thisCommand = "WiReQRY"
1466
        pollTimer = os.startTimer(3.25)
1467
      end
1468
    elseif timer == closeTimer then
1469
      if not ccSettings.lockState then
1470
        thisCommand = deviceType == "Energy" and "ON" or "CLOSED"
1471
      end
1472
    end
1473
    if thisCommand == "WiReQRY" or thisCommand == "ON" or thisCommand == "CLOSED" then
1474
      ccSuccess = doAction()
1475
      if thisCommand == "WiReQRY" then
1476
        thisTime = tostring(os.time())
1477
        if (tonumber(thisCC) % 2 == 0 and tonumber(thisTime:sub(#thisTime)) % 2 == 0) or (tonumber(thisCC) % 2 ~= 0 and tonumber(thisTime:sub(#thisTime)) % 2 ~= 0) then
1478
          if not tArgs[1] then
1479
            term.setCursorPos(41, 17)
1480
            term.setTextColor(green)
1481
            term.write("True ")
1482
          end
1483
          netSend()
1484
        else
1485
          if not tArgs[1] then
1486
            term.setCursorPos(41, 17)
1487
            term.setTextColor(red)
1488
            term.write("False")
1489
          end
1490
          os.cancelTimer(pollTimer)
1491
          pollTimer = os.startTimer(0.25)
1492
        end
1493
      else
1494
        netSend()
1495
      end
1496
    end
1497
    if thisCommand ~= "Noise" then updateScreens() end
1498
  end
1499
end
1500
1501
local function userInput()
1502
  local event, data
1503
  while true do
1504
    event, data = os.pullEvent()
1505
    if event == "key" and data == keys.f1 then
1506
      help = not help
1507
      if help then
1508
        helpScreen()
1509
      else
1510
        term.setBackgroundColor(black)
1511
        term.clear()
1512
        staticTermScreen()
1513
        termScreen()
1514
      end
1515
    elseif event == "char" and data:lower() == "q" then
1516
      shutDown()
1517
      clearTerm()
1518
      term.write("WiRe Client is OFFLINE")
1519
      term.setCursorPos(1, 3)
1520
      return
1521
    end
1522
  end
1523
end
1524
1525
local function monTouch()
1526
  while true do
1527
    os.pullEvent("monitor_touch")
1528
    if not ccSettings.lockState then
1529
      thisCommand = validStates[deviceState] or "Touch"
1530
      ccSuccess = doAction()
1531
      netSend()
1532
      updateScreens()
1533
    end
1534
  end
1535
end
1536
1537
local function initError(missing, device)
1538
  if modemSide and rednet.isOpen(modemSide) then rednet.close(modemSide) end
1539
  if mon[1] then clearMonitors() end
1540
  term.clear()
1541
  term.setTextColor(red)
1542
  term.setCursorPos(1, 2)
1543
  print("No " .. missing .. " detected!")
1544
  print("WiRe Client REQUIRES")
1545
  print(device .. ".")
1546
  term.setTextColor(white)
1547
  term.setCursorPos(1, 6)
1548
end
1549
1550
local function firstRun()
1551
  term.clear()
1552
  local gotModem = false
1553
  for _, side in pairs(rs.getSides()) do
1554
    if peripheral.isPresent(side) and peripheral.getType(side) == "modem" then
1555
      gotModem = true
1556
      break
1557
    end
1558
  end
1559
  if not gotModem then return initError("modem", "a modem") end
1560
  --# Set computer name
1561
  term.setCursorPos(2, 2)
1562
  term.write("Please name this device")
1563
  term.setCursorPos(3, 3)
1564
  term.write("(12 characters or less)")
1565
  repeat
1566
    term.setCursorPos(2, 4)
1567
    local newName = read()
1568
    newName = newName and newName:sub(1, 12) or ""
1569
    ccSettings.name = newName
1570
  until newName ~= ""
1571
  --# Set computer label
1572
  if not os.getComputerLabel() then
1573
    os.setComputerLabel(ccSettings.name)
1574
  end
1575
  --# Set computer description
1576
  term.clear()
1577
  term.setCursorPos(2, 2)
1578
  term.write("Please type in a short")
1579
  term.setCursorPos(2, 3)
1580
  term.write("client description")
1581
  repeat
1582
    term.setCursorPos(2, 5)
1583
    local newDesc = read()
1584
    ccSettings.note = newDesc
1585
  until newDesc ~= ""
1586
  --# Select device type
1587
  local deviceTypes = {
1588
    c = { "Create", false, true };
1589
    d = { "Door", false, true };
1590
    f = { "eDoor", true, false };
1591
    p = { "Piston", true, false };
1592
    e = { "Energy", true, false };
1593
  }
1594
  term.clear()
1595
  term.setCursorPos(2, 2)
1596
  term.write("Is this a Create device?          [c]")
1597
  term.setCursorPos(2, 3)
1598
  term.write("Is this a physical Door/Hatch?    [d]")
1599
  term.setCursorPos(2, 4)
1600-
  term.setCursorPos(2, 7)
1600+
1601
  term.setCursorPos(2, 5)
1602-
  term.setCursorPos(2, 9)
1602+
1603
  term.setCursorPos(2, 6)
1604
  term.write("Energy Device/Barrier/Lamp/Other? [e]")
1605
  term.setCursorPos(2, 8)
1606
  term.write("[d/f/p] - States are 'OPEN/CLOSED'")
1607
  term.setCursorPos(2, 10)
1608-
  term.setCursorPos(2, 13)
1608+
1609
  term.setCursorPos(2, 11)
1610-
  term.setCursorPos(2, 15)
1610+
1611
  term.setCursorPos(2, 12)
1612
  term.write("Piston: OPEN = RS(false) / CLOSED = RS(true)")
1613-
    term.setCursorPos(2, 17)
1613+
  term.setCursorPos(2, 14)
1614
  term.write("[e] - States are 'OFF/ON'")
1615
  term.setCursorPos(2, 16)
1616
  term.write("Energy: OFF = RS(false) / ON = RS(true)")
1617
  repeat
1618
    term.setCursorPos(2, 18)
1619
    local newType = string.lower(read())
1620
    if deviceTypes[newType] then
1621
      deviceType = deviceTypes[newType][1]
1622
      onState = deviceTypes[newType][2]
1623
      offState = deviceTypes[newType][3]
1624
      ccSettings.onState = onState
1625
      ccSettings.offState = offState
1626
      ccSettings.deviceType = deviceType
1627
    end
1628
  until deviceTypes[newType]
1629
  --# Select auto-close setting
1630
  term.clear()
1631
  term.setCursorPos(2, 2)
1632
  if deviceType == "Door" or deviceType == "eDoor" or deviceType == "Piston" then
1633
    term.write("Would you like the door or piston to")
1634
    term.setCursorPos(2, 3)
1635
    term.write("automatically close shortly after")
1636
    term.setCursorPos(2, 4)
1637
    term.write("being opened? [y/n]")
1638
  else
1639
    term.write("Would you like the device to")
1640
    term.setCursorPos(2, 3)
1641
    term.write("automatically re-activate shortly")
1642
    term.setCursorPos(2, 4)
1643
    term.write("after being deactivated? [y/n]")
1644
  end
1645
  term.setCursorPos(2, 6)
1646
  local closeRule = string.lower(read())
1647
  autoClose = closeRule:sub(1, 1) == "y"
1648
  ccSettings.autoClose = autoClose
1649
  --# Select auto-close delay
1650
  if autoClose then
1651
    term.clear()
1652
    term.setCursorPos(2, 2)
1653
    term.write("How many seconds should the")
1654
    term.setCursorPos(2, 3)
1655
    if deviceType == "Door" or deviceType == "eDoor" or deviceType == "Piston" then
1656
      term.write("door or piston stay open before closing")
1657
    else
1658
      term.write("device stay off before reactivating")
1659
    end
1660
    term.setCursorPos(2, 4)
1661
    term.write("automatically?")
1662
    repeat
1663
      term.setCursorPos(2, 6)
1664
      local closeTime = tonumber(read())
1665
      if closeTime then
1666
        autoDelay = closeTime
1667
        ccSettings.autoDelay = autoDelay
1668
      end
1669
    until closeTime
1670
  end
1671
  --# Select default startup state
1672
  term.clear()
1673
  term.setCursorPos(2, 1)
1674
  term.write("What would you like the default")
1675
  term.setCursorPos(2, 2)
1676
  term.write("STARTUP state to be?")
1677
  term.setCursorPos(2, 4)
1678
  if deviceType == "Door" or deviceType == "eDoor" or deviceType == "Piston" then
1679
    term.write("[p] Previous State or [c] CLOSED or [o] OPEN")
1680
  else
1681
    term.write("[p] Previous State or [n] ON or [f] OFF")
1682
  end
1683
  while true do
1684
    term.setCursorPos(2, 5)
1685
    local newStart = string.lower(read())
1686
    if newStart == "o" or newStart == "c" or newStart == "n" or newStart == "f" or newStart == "p" then
1687
      if newStart == "c" or newStart == "n" then
1688
        defaultStart = onState
1689
      elseif newStart == "o" or newStart == "f" then
1690
        defaultStart = offState
1691
      elseif newStart == "p" then
1692
        defaultStart = "last"
1693
        ccSettings.lastState = false
1694
      end
1695
      ccSettings.defaultStart = defaultStart
1696
      break
1697
    end
1698
  end
1699
  --# Select color
1700
  local colorWheel = {
1701
    P = "Purple";
1702
    M = "Magenta";
1703
    B = "Blue";
1704
    S = "Sky";
1705
    C = "Cyan";
1706
    E = "Green";
1707
    L = "Lime";
1708
    R = "Red";
1709
    O = "Orange";
1710
    Y = "Yellow";
1711
    N = "Brown";
1712
    K = "Black";
1713
    G = "Gray";
1714
    I = "Silver";
1715
    W = "White";
1716
  }
1717
  term.clear()
1718
  term.setCursorPos(2, 1)
1719
  term.write("Please select the network group")
1720
  term.setCursorPos(2, 2)
1721
  if deviceType == "Door" or device == "eDoor" then
1722
    term.write("this door will belong to...")
1723
  else
1724
    term.write("this device will belong to...")
1725
  end
1726
  local ttY = 4
1727
  for k, v in pairs(colorWheel) do
1728
    term.setCursorPos(2, ttY)
1729
    term.write(k .. " = " .. v)
1730
    ttY = ttY + 1
1731
  end
1732
  repeat
1733
    term.setCursorPos(32, 2)
1734
    local newColor = string.upper(read())
1735
    ccSettings.color = colorWheel[newColor:sub(1, 1)]
1736
  until colorWheel[newColor:sub(1, 1)]
1737
  --# Select redstone output side
1738
  local theseSides = { "top", "bottom", "left", "right", "front", "back" }
1739
  term.clear()
1740
  term.setCursorPos(2, 2)
1741
  term.write("Select the redstone output side")
1742
  local yPos = 3
1743
  for num, side in pairs(theseSides) do
1744
    yPos = yPos + 1
1745
    term.setCursorPos(2, yPos)
1746
    term.write(tostring(num) .. " = " .. string.upper(side:sub(1, 1)) .. side:sub(2))
1747
  end
1748
  repeat
1749
    term.setCursorPos(2, 11)
1750
    local rsSide = tonumber(read())
1751
    ccSettings.side = theseSides[rsSide]
1752
  until theseSides[rsSide]
1753
  --# Should WiRe client get a GPS fix on startup?
1754
  term.clear()
1755
  term.setCursorPos(2, 1)
1756
  term.write("Last question!")
1757
  term.setCursorPos(2, 3)
1758
  term.write("Do you want WiRe client to")
1759
  term.setCursorPos(2, 4)
1760
  term.write("aquire a GPS fix on startup? [y/n]")
1761
  term.setCursorPos(2, 6)
1762
  local getFix = string.lower(read())
1763
  ccSettings.getGPSFix = getFix:sub(1, 1) == "y"
1764
  if not fs.exists("/data") then fs.makeDir("/data") end
1765
  saveData()
1766
  return true
1767
end
1768
1769
term.setBackgroundColor(black)
1770
if pocket or turtle then error("Computer REQUIRED.", 0) end
1771
if not fs.exists(config) then
1772
  if not firstRun() then return end
1773
end
1774
term.clear()
1775
term.setCursorPos(2, 2)
1776
term.setTextColor(white)
1777
term.write("Ingesting configuration data . . .")
1778
local clientConfig = fs.open(config, "r") or error("initMe(): Cannot open " .. config .. " for reading", 0)
1779
ccSettings = textutils.unserialize(clientConfig.readAll())
1780
clientConfig.close()
1781
if ccSettings.getFix ~= nil or ccSettings.deviceType == "Bridge" or not ccSettings.newColors then
1782
  if ccSettings.getFix ~= nil then
1783
    ccSettings.getGPSFix = ccSettings.getFix
1784
    ccSettings.getFix = nil
1785
  end
1786
  if ccSettings.deviceType == "Bridge" then
1787
    ccSettings.deviceType = "Energy"
1788
  end
1789
  if not ccSettings.newColors then
1790
    if ccSettings.color == "lgray" or ccSettings.color == "Light Gray" then
1791
      ccSettings.color = "Silver"
1792
    elseif ccSettings.color == "lblue" or ccSettings.color == "Light Blue" then
1793
      ccSettings.color = "Sky"
1794
    end
1795
    ccSettings.newColors = true
1796
  end
1797
  saveData()
1798
end
1799
term.setCursorPos(2, 4)
1800
term.write("Configuring hardware . . .")
1801
for _, side in pairs(rs.getSides()) do
1802
  if peripheral.isPresent(side) then
1803
    if peripheral.getType(side) == "monitor" and peripheral.call(side, "isColor") then
1804
      mon[#mon + 1] = peripheral.wrap(side)
1805
    elseif peripheral.getType(side) == "modem" then
1806
      if peripheral.call(side, "isWireless") then
1807
        modemSide = side
1808
        if not rednet.isOpen(side) then rednet.open(side) end
1809
      else
1810
        for _, perp in pairs(peripheral.call(side, "getNamesRemote")) do
1811
          if peripheral.getType(perp) == "monitor" and peripheral.call(perp, "isColor") then
1812
            mon[#mon + 1] = peripheral.wrap(perp)
1813
          elseif peripheral.getType(perp) == "computer" then
1814
            modemSide = side
1815
            if not rednet.isOpen(side) then rednet.open(side) end
1816
          end
1817
        end
1818
      end
1819
    end
1820
  end
1821
end
1822
if not modemSide then return initError("modem", "a modem") end
1823
if mon[1] then
1824
  for i = 1, #mon do
1825
    mon[i].setTextScale(0.5)
1826
    mon[i].setBackgroundColor(mwhite)
1827
    mon[i].setTextColor(mblack)
1828
    mon[i].clear()
1829
    mon[i].setCursorPos(2, 3)
1830
    mon[i].write("Initializing...")
1831
  end
1832
end
1833
term.setCursorPos(2, 6)
1834
if ccSettings.getGPSFix then
1835
  term.write("Acquiring GPS fix . . .")
1836
  loc.x, loc.y, loc.z = gps.locate(2)
1837
  term.setCursorPos(2, 8)
1838
end
1839
if not loc.x then
1840
  loc.x, loc.y, loc.z = "No GPS Fix", "No GPS Fix", "No GPS Fix"
1841
end
1842
term.write("Looking for WiRe Server . . .")
1843
network = "WiRe" .. ccSettings.color
1844
for i = 1, 3 do
1845
  server = rednet.lookup(network, ccSettings.color)
1846
  if server then break elseif i ~= 3 then sleep(2) end
1847
end
1848
if not server then
1849
  term.clear()
1850
  term.setTextColor(red)
1851
  term.setCursorPos(1, 2)
1852
  print("No server detected!")
1853
  print("WiRe Client REQUIRES a WiRe Server.\n")
1854
  print("Output set to 'Default Start'")
1855
  if rednet.isOpen(modemSide) then rednet.close(modemSide) end
1856
  if mon[1] then clearMonitors() end
1857
  term.setCursorPos(1, 9)
1858
end
1859
deviceType = ccSettings.deviceType
1860
onState = ccSettings.onState
1861
offState = ccSettings.offState
1862
defaultStart = ccSettings.defaultStart
1863
autoClose = ccSettings.autoClose
1864
autoDelay = ccSettings.autoDelay
1865
local startState = false
1866
if type(defaultStart) == "string" then
1867
  startState = ccSettings.lastState
1868
else
1869
  startState = defaultStart
1870
end
1871
rs.setOutput(ccSettings.side, startState)
1872
if not server then return end
1873
if deviceType == "Door" then
1874
  deviceState = startState and "OPEN" or "CLOSED"
1875
elseif deviceType == "Piston" or deviceType == "eDoor" then
1876
  deviceState = startState and "CLOSED" or "OPEN"
1877
else
1878
  deviceState = startState and "ON" or "OFF"
1879
end
1880
if autoClose and (deviceState == "OPEN" or deviceState == "OFF") then closeTimer = os.startTimer(autoDelay) end
1881
netSend()
1882
thisCommand = "init"
1883
ccSuccess, kernelState = true, true
1884
for i = 1, #mon do
1885
  mon[i].setTextScale(1)
1886
  mon[i].setBackgroundColor(mblack)
1887
  mon[i].clear()
1888
end
1889
term.clear()
1890
if not tArgs[1] then staticTermScreen() updateScreens() end
1891
pollTimer = os.startTimer(5)
1892
if tArgs[1] then
1893
  parallel.waitForAny(netReceive, dataPoller, monTouch, foregroundShell)
1894
else
1895
  parallel.waitForAny(netReceive, dataPoller, monTouch, userInput)
1896
end