· 6 years ago · Dec 28, 2019, 11:48 AM
1--[[ WiRe Client ]]--
2--[[ by Dog ]]--
3--[[ aka HydrantHunter ]]--
4--[[ Wi = Wireless ]]--
5--[[ Re = Redstone ]]--
6--[[ pastebin jtFa7V1n ]]--
7local WiReCver = "2.0.05"
8--[[
9Tested 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
15Special thanks to: SquidDev (AES encryption/decryption)
16 Alex Kloss (base64 encoder/decoder)
17]]--
18local tArgs = { ... }
19--# CONFIGURATION
20--# Default Settings
21local termX, termY = term.getSize()
22local thisCC = tostring(os.getComputerID())
23local config = "/data/WiReClientCfg"
24local 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}
40local validStates = {
41 ON = "OFF";
42 OFF = "ON";
43 OPEN = "CLOSED";
44 CLOSED = "OPEN";
45 LOCKED = true;
46 UNLOCK = true;
47 WiReQRY = true;
48}
49local mon, loc = { }, { }
50local deviceState, deviceType, autoDelay = "QRY", "QRY", 0
51local defaultStart, onState, offState, defaultLock, defaultUnlock = false, false, true, false, false
52local kernelState, ccSuccess, autoClose, help = false, false, false, false
53local network, server, modemSide, thisCommand, pollTimer, updateScreens, staticTermScreen
54--# Terminal Colors
55local white = colors.white
56local black = colors.black
57local silver = colors.lightGray
58local gray = colors.gray
59local brown = colors.brown
60local yellow = colors.yellow
61local orange = colors.orange
62local red = colors.red
63local magenta = colors.magenta
64local purple = colors.purple
65local blue = colors.blue
66local sky = colors.lightBlue
67local cyan = colors.cyan
68local lime = colors.lime
69local green = colors.green
70if 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
85end
86--# Monitor colors
87local mwhite = colors.white
88local mblack = colors.black
89local msilver = colors.lightGray
90local mgray = colors.gray
91local mbrown = colors.brown
92local myellow = colors.yellow
93local morange = colors.orange
94local mred = colors.red
95local mmagenta = colors.magenta
96local mpurple = colors.purple
97local mblue = colors.blue
98local msky = colors.lightBlue
99local mcyan = colors.cyan
100local mlime = colors.lime
101local mgreen = colors.green
102--# Status colors
103local 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
115local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
116-- encoding
117function 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])
128end
129-- decoding
130function 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))
143end
144
145-- AES Lua implementation by SquidDev
146-- https://gist.github.com/SquidDev/86925e07cbabd70773e53d781bd8b2fe
147local encrypt, decrypt
148do
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
1172end
1173
1174local 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()
1178end
1179
1180local 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 else
1208 deviceState = rsOut and "ON" or "OFF"
1209 end
1210 return true
1211 end
1212 return false
1213end
1214
1215local function netSend()
1216 local dataPack = textutils.serialize({
1217 program = "WiRe";
1218 cc = tonumber(thisCC);
1219 name = ccSettings.name;
1220 color = ccSettings.color;
1221 deviceType = deviceType;
1222 deviceState = deviceState;
1223 lockState = ccSettings.lockState;
1224 quietCount = 0;
1225 loc = loc;
1226 })
1227 if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
1228 local encKey = tostring(server) .. "WiRe!Comms" .. thisCC
1229 local encryptedPackage = encode(encrypt(encKey, dataPack))
1230 rednet.send(server, encryptedPackage, network)
1231end
1232
1233local function netReceive()
1234 local id, newCmdData, goodData, encryptedMessage, decryptedMessage, decodedMessage, encKey, success
1235 while true do
1236 if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
1237 newCmdData = { }
1238 id, encryptedMessage = rednet.receive(network)
1239 goodData = false
1240 if type(encryptedMessage) == "string" then
1241 success, decodedMessage = pcall(decode, encryptedMessage)
1242 if success and type(decodedMessage) == "string" then
1243 encKey = thisCC .. "WiRe!Comms" .. tostring(id)
1244 success, decryptedMessage = pcall(decrypt, encKey, decodedMessage)
1245 if success and type(decryptedMessage) == "string" then
1246 success, newCmdData = pcall(textutils.unserialize, decryptedMessage)
1247 if success and type(newCmdData) == "table" and newCmdData.program then
1248 if newCmdData.program == "WiRe" and newCmdData.cc == id and id == server and newCmdData.color == ccSettings.color then
1249 if validStates[newCmdData.cmd] then
1250 thisCommand = newCmdData.cmd
1251 goodData = true
1252 end
1253 end
1254 end
1255 else
1256 encKey = tostring(id) .. "WiRe!Comms" .. tostring(id)
1257 success, decryptedMessage = pcall(decrypt, encKey, decodedMessage)
1258 if success and type(decryptedMessage) == "string" then
1259 success, newCmdData = pcall(textutils.unserialize, decryptedMessage)
1260 if success and type(newCmdData) == "table" and newCmdData.program then
1261 if newCmdData.program == "WiRe" and newCmdData.cc == id and id == server and newCmdData.color == ccSettings.color then
1262 if validStates[newCmdData.cmd] then
1263 thisCommand = newCmdData.cmd
1264 goodData = true
1265 end
1266 end
1267 end
1268 end
1269 end
1270 end
1271 end
1272 if goodData then
1273 ccSuccess = doAction()
1274 netSend()
1275 else
1276 thisCommand = "Noise"
1277 ccSuccess = false
1278 end
1279 updateScreens()
1280 end
1281end
1282
1283do
1284 local colorBurst = {
1285 Purple = purple;
1286 Magenta = magenta;
1287 Blue = blue;
1288 Sky = sky;
1289 Cyan = cyan;
1290 Green = green;
1291 Lime = lime;
1292 Red = red;
1293 Orange = orange;
1294 Yellow = yellow;
1295 Brown = brown;
1296 Silver = silver;
1297 Gray = gray;
1298 White = white;
1299 Black = white;
1300 }
1301
1302 local labels = {
1303 [1] = { "Name", 1, 3 };
1304 [2] = { "Note:", 1, 4 };
1305 [3] = { "Group:", 1, 6 };
1306 [4] = { "cc#", 1, 8 };
1307 [5] = { "Server cc#", 1, 9 };
1308 [6] = { "Modem:", 1, 11 };
1309 [7] = { "Redstone:", 1, 12 };
1310 [8] = { "Device Type:", 1, 14 };
1311 [9] = { "Current State:", 1, 15 };
1312 [10] = { "Last Command:", 1, 17 };
1313 [11] = { "Success:", 1, 18 };
1314 [12] = { "Peripherals", 28, 3 };
1315 [13] = { "Location: x:", 28, 11 };
1316 [14] = { "y:", 38, 12 };
1317 [15] = { "z:", 38, 13 };
1318 [16] = { "Check-in:", 31, 17 };
1319 }
1320
1321 staticTermScreen = function()
1322 local hText = "WiRe Client " .. WiReCver
1323 local spacer = (termX - #hText) / 2
1324 term.setBackgroundColor(blue)
1325 term.setTextColor(white)
1326 term.setCursorPos(1, 1)
1327 term.write(string.rep(" ", math.floor(spacer)) .. hText .. string.rep(" ", math.ceil(spacer)))
1328 term.setBackgroundColor(black)
1329 term.setTextColor(silver)
1330 for i = 1, 16 do
1331 term.setCursorPos(labels[i][2], labels[i][3])
1332 term.write(labels[i][1])
1333 end
1334 term.setTextColor(white)
1335 term.setCursorPos(7, 3)
1336 term.write(ccSettings.name)
1337 term.setCursorPos(7, 4)
1338 term.write(ccSettings.note)
1339 term.setCursorPos(5, 8)
1340 term.write(thisCC)
1341 term.setCursorPos(12, 9)
1342 term.write(tostring(server))
1343 term.setCursorPos(11, 11)
1344 term.write(modemSide)
1345 term.setCursorPos(11, 12)
1346 term.write(ccSettings.side)
1347 term.setCursorPos(16, 14)
1348 term.write(deviceType)
1349 if mon[1] then
1350 term.setCursorPos(28, 5)
1351 term.write("Monitor x" .. tostring(#mon))
1352 end
1353 term.setCursorPos(41, 11)
1354 term.write(tostring(loc.x))
1355 term.setCursorPos(41, 12)
1356 term.write(tostring(loc.y))
1357 term.setCursorPos(41, 13)
1358 term.write(tostring(loc.z))
1359 term.setTextColor(colorBurst[ccSettings.color] or silver)
1360 term.setCursorPos(8, 6)
1361 term.write(ccSettings.color)
1362 end
1363end
1364
1365local function termScreen()
1366 term.setCursorPos(16, 15)
1367 term.setTextColor(switchStates[deviceState][1] or yellow)
1368 term.write(deviceState)
1369 term.setTextColor(gray)
1370 term.write(" | ")
1371 term.setTextColor(ccSettings.lockState and red or green)
1372 term.write(ccSettings.lockState and "Locked " or "Unlocked ")
1373 term.setCursorPos(15, 17)
1374 term.setTextColor(white)
1375 term.write(thisCommand .. string.rep(" ", 8))
1376 term.setCursorPos(15, 18)
1377 term.setTextColor(ccSuccess and green or red)
1378 term.write(tostring(ccSuccess) .. " ")
1379end
1380
1381local function helpScreen()
1382 local hText = "WiRe Client Help"
1383 local xPos, spacer = math.floor(termX / 2), (termX - #hText) / 2
1384 term.setBackgroundColor(white)
1385 term.clear()
1386 term.setBackgroundColor(blue)
1387 term.setTextColor(white)
1388 term.setCursorPos(1, 1)
1389 term.write(string.rep(" ", math.floor(spacer)) .. hText .. string.rep(" ", math.ceil(spacer)))
1390 term.setBackgroundColor(white)
1391 term.setTextColor(black)
1392 term.setCursorPos(xPos - 8, 5)
1393 term.write("'q' to quit client")
1394 term.setCursorPos(xPos - 12, 7)
1395 term.write("'F1' to display/exit help")
1396end
1397
1398do
1399 local function touchScreen()
1400 local line, symbol = " "
1401 for i = 1, #mon do
1402 symbol = ccSettings.lockState and " [0] " or (switchStates[deviceState][3] or " [X] ")
1403 mon[i].setBackgroundColor(ccSettings.lockState and mred or (switchStates[deviceState][2] or myellow))
1404 mon[i].setTextColor(mwhite)
1405 for y = 2, 4 do
1406 mon[i].setCursorPos(2, y)
1407 mon[i].write(y == 3 and symbol or line)
1408 end
1409 end
1410 end
1411
1412 updateScreens = function()
1413 if not tArgs[1] and not help then termScreen() end
1414 if thisCommand ~= "Noise" and mon[1] then touchScreen() end
1415 end
1416end
1417
1418local function clearMonitors()
1419 for i = 1, #mon do
1420 mon[i].setBackgroundColor(mblack)
1421 mon[i].clear()
1422 end
1423end
1424
1425local function clearTerm()
1426 term.setBackgroundColor(black)
1427 term.setTextColor(white)
1428 term.clear()
1429 term.setCursorPos(1, 1)
1430end
1431
1432local function shutDown()
1433 deviceState = "OFFLINE"
1434 kernelState = false
1435 netSend()
1436 if rednet.isOpen(modemSide) then rednet.close(modemSide) end
1437 if mon[1] then clearMonitors() end
1438end
1439
1440local function foregroundShell()
1441 clearTerm()
1442 if fs.exists(tArgs[1]) then
1443 local unpack = unpack or table.unpack
1444 shell.run(unpack(tArgs))
1445 clearTerm()
1446 else
1447 term.write(tArgs[1] .. " missing")
1448 term.setCursorPos(1, 3)
1449 end
1450 shutDown()
1451 term.write("WiRe Client is OFFLINE")
1452 term.setCursorPos(1, 5)
1453end
1454
1455local function dataPoller()
1456 local _, timer, thisTime
1457 while true do
1458 _, timer = os.pullEvent("timer")
1459 thisCommand = "Noise"
1460 ccSuccess = false
1461 if timer == pollTimer then
1462 if kernelState then
1463 thisCommand = "WiReQRY"
1464 pollTimer = os.startTimer(3.25)
1465 end
1466 elseif timer == closeTimer then
1467 if not ccSettings.lockState then
1468 thisCommand = deviceType == "Energy" and "ON" or "CLOSED"
1469 end
1470 end
1471 if thisCommand == "WiReQRY" or thisCommand == "ON" or thisCommand == "CLOSED" then
1472 ccSuccess = doAction()
1473 if thisCommand == "WiReQRY" then
1474 thisTime = tostring(os.time())
1475 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
1476 if not tArgs[1] then
1477 term.setCursorPos(41, 17)
1478 term.setTextColor(green)
1479 term.write("True ")
1480 end
1481 netSend()
1482 else
1483 if not tArgs[1] then
1484 term.setCursorPos(41, 17)
1485 term.setTextColor(red)
1486 term.write("False")
1487 end
1488 os.cancelTimer(pollTimer)
1489 pollTimer = os.startTimer(0.25)
1490 end
1491 else
1492 netSend()
1493 end
1494 end
1495 if thisCommand ~= "Noise" then updateScreens() end
1496 end
1497end
1498
1499local function userInput()
1500 local event, data
1501 while true do
1502 event, data = os.pullEvent()
1503 if event == "key" and data == keys.f1 then
1504 help = not help
1505 if help then
1506 helpScreen()
1507 else
1508 term.setBackgroundColor(black)
1509 term.clear()
1510 staticTermScreen()
1511 termScreen()
1512 end
1513 elseif event == "char" and data:lower() == "q" then
1514 shutDown()
1515 clearTerm()
1516 term.write("WiRe Client is OFFLINE")
1517 term.setCursorPos(1, 3)
1518 return
1519 end
1520 end
1521end
1522
1523local function monTouch()
1524 while true do
1525 os.pullEvent("monitor_touch")
1526 if not ccSettings.lockState then
1527 thisCommand = validStates[deviceState] or "Touch"
1528 ccSuccess = doAction()
1529 netSend()
1530 updateScreens()
1531 end
1532 end
1533end
1534
1535local function initError(missing, device)
1536 if modemSide and rednet.isOpen(modemSide) then rednet.close(modemSide) end
1537 if mon[1] then clearMonitors() end
1538 term.clear()
1539 term.setTextColor(red)
1540 term.setCursorPos(1, 2)
1541 print("No " .. missing .. " detected!")
1542 print("WiRe Client REQUIRES")
1543 print(device .. ".")
1544 term.setTextColor(white)
1545 term.setCursorPos(1, 6)
1546end
1547
1548local function firstRun()
1549 term.clear()
1550 local gotModem = false
1551 for _, side in pairs(rs.getSides()) do
1552 if peripheral.isPresent(side) and peripheral.getType(side) == "modem" then
1553 gotModem = true
1554 break
1555 end
1556 end
1557 if not gotModem then return initError("modem", "a modem") end
1558 --# Set computer name
1559 term.setCursorPos(2, 2)
1560 term.write("Please name this device")
1561 term.setCursorPos(3, 3)
1562 term.write("(12 characters or less)")
1563 repeat
1564 term.setCursorPos(2, 4)
1565 local newName = read()
1566 newName = newName and newName:sub(1, 12) or ""
1567 ccSettings.name = newName
1568 until newName ~= ""
1569 --# Set computer label
1570 if not os.getComputerLabel() then
1571 os.setComputerLabel(ccSettings.name)
1572 end
1573 --# Set computer description
1574 term.clear()
1575 term.setCursorPos(2, 2)
1576 term.write("Please type in a short")
1577 term.setCursorPos(2, 3)
1578 term.write("client description")
1579 repeat
1580 term.setCursorPos(2, 5)
1581 local newDesc = read()
1582 ccSettings.note = newDesc
1583 until newDesc ~= ""
1584 --# Select device type
1585 local deviceTypes = {
1586 d = { "Door", false, true };
1587 f = { "eDoor", true, false };
1588 p = { "Piston", true, false };
1589 e = { "Energy", true, false };
1590 }
1591 term.clear()
1592 term.setCursorPos(2, 2)
1593 term.write("Is this a physical Door/Hatch? [d]")
1594 term.setCursorPos(2, 3)
1595 term.write("A Forcefield or Energy Door? [f]")
1596 term.setCursorPos(2, 4)
1597 term.write("A Piston door or Piston hatch? [p]")
1598 term.setCursorPos(2, 5)
1599 term.write("Energy Device/Barrier/Lamp/Other? [e]")
1600 term.setCursorPos(2, 7)
1601 term.write("[d/f/p] - States are 'OPEN/CLOSED'")
1602 term.setCursorPos(2, 9)
1603 term.write("Door: OPEN = RS(true) / CLOSED = RS(false)")
1604 term.setCursorPos(2, 10)
1605 term.write("eDoor: OPEN = RS(false) / CLOSED = RS(true)")
1606 term.setCursorPos(2, 11)
1607 term.write("Piston: OPEN = RS(false) / CLOSED = RS(true)")
1608 term.setCursorPos(2, 13)
1609 term.write("[e] - States are 'OFF/ON'")
1610 term.setCursorPos(2, 15)
1611 term.write("Energy: OFF = RS(false) / ON = RS(true)")
1612 repeat
1613 term.setCursorPos(2, 17)
1614 local newType = string.lower(read())
1615 if deviceTypes[newType] then
1616 deviceType = deviceTypes[newType][1]
1617 onState = deviceTypes[newType][2]
1618 offState = deviceTypes[newType][3]
1619 ccSettings.onState = onState
1620 ccSettings.offState = offState
1621 ccSettings.deviceType = deviceType
1622 end
1623 until deviceTypes[newType]
1624 --# Select auto-close setting
1625 term.clear()
1626 term.setCursorPos(2, 2)
1627 if deviceType == "Door" or deviceType == "eDoor" or deviceType == "Piston" then
1628 term.write("Would you like the door or piston to")
1629 term.setCursorPos(2, 3)
1630 term.write("automatically close shortly after")
1631 term.setCursorPos(2, 4)
1632 term.write("being opened? [y/n]")
1633 else
1634 term.write("Would you like the device to")
1635 term.setCursorPos(2, 3)
1636 term.write("automatically re-activate shortly")
1637 term.setCursorPos(2, 4)
1638 term.write("after being deactivated? [y/n]")
1639 end
1640 term.setCursorPos(2, 6)
1641 local closeRule = string.lower(read())
1642 autoClose = closeRule:sub(1, 1) == "y"
1643 ccSettings.autoClose = autoClose
1644 --# Select auto-close delay
1645 if autoClose then
1646 term.clear()
1647 term.setCursorPos(2, 2)
1648 term.write("How many seconds should the")
1649 term.setCursorPos(2, 3)
1650 if deviceType == "Door" or deviceType == "eDoor" or deviceType == "Piston" then
1651 term.write("door or piston stay open before closing")
1652 else
1653 term.write("device stay off before reactivating")
1654 end
1655 term.setCursorPos(2, 4)
1656 term.write("automatically?")
1657 repeat
1658 term.setCursorPos(2, 6)
1659 local closeTime = tonumber(read())
1660 if closeTime then
1661 autoDelay = closeTime
1662 ccSettings.autoDelay = autoDelay
1663 end
1664 until closeTime
1665 end
1666 --# Select default startup state
1667 term.clear()
1668 term.setCursorPos(2, 1)
1669 term.write("What would you like the default")
1670 term.setCursorPos(2, 2)
1671 term.write("STARTUP state to be?")
1672 term.setCursorPos(2, 4)
1673 if deviceType == "Door" or deviceType == "eDoor" or deviceType == "Piston" then
1674 term.write("[p] Previous State or [c] CLOSED or [o] OPEN")
1675 else
1676 term.write("[p] Previous State or [n] ON or [f] OFF")
1677 end
1678 while true do
1679 term.setCursorPos(2, 5)
1680 local newStart = string.lower(read())
1681 if newStart == "o" or newStart == "c" or newStart == "n" or newStart == "f" or newStart == "p" then
1682 if newStart == "c" or newStart == "n" then
1683 defaultStart = onState
1684 elseif newStart == "o" or newStart == "f" then
1685 defaultStart = offState
1686 elseif newStart == "p" then
1687 defaultStart = "last"
1688 ccSettings.lastState = false
1689 end
1690 ccSettings.defaultStart = defaultStart
1691 break
1692 end
1693 end
1694 --# Select color
1695 local colorWheel = {
1696 P = "Purple";
1697 M = "Magenta";
1698 B = "Blue";
1699 S = "Sky";
1700 C = "Cyan";
1701 E = "Green";
1702 L = "Lime";
1703 R = "Red";
1704 O = "Orange";
1705 Y = "Yellow";
1706 N = "Brown";
1707 K = "Black";
1708 G = "Gray";
1709 I = "Silver";
1710 W = "White";
1711 }
1712 term.clear()
1713 term.setCursorPos(2, 1)
1714 term.write("Please select the network group")
1715 term.setCursorPos(2, 2)
1716 if deviceType == "Door" or device == "eDoor" then
1717 term.write("this door will belong to...")
1718 else
1719 term.write("this device will belong to...")
1720 end
1721 local ttY = 4
1722 for k, v in pairs(colorWheel) do
1723 term.setCursorPos(2, ttY)
1724 term.write(k .. " = " .. v)
1725 ttY = ttY + 1
1726 end
1727 repeat
1728 term.setCursorPos(32, 2)
1729 local newColor = string.upper(read())
1730 ccSettings.color = colorWheel[newColor:sub(1, 1)]
1731 until colorWheel[newColor:sub(1, 1)]
1732 --# Select redstone output side
1733 local theseSides = { "top", "bottom", "left", "right", "front", "back" }
1734 term.clear()
1735 term.setCursorPos(2, 2)
1736 term.write("Select the redstone output side")
1737 local yPos = 3
1738 for num, side in pairs(theseSides) do
1739 yPos = yPos + 1
1740 term.setCursorPos(2, yPos)
1741 term.write(tostring(num) .. " = " .. string.upper(side:sub(1, 1)) .. side:sub(2))
1742 end
1743 repeat
1744 term.setCursorPos(2, 11)
1745 local rsSide = tonumber(read())
1746 ccSettings.side = theseSides[rsSide]
1747 until theseSides[rsSide]
1748 --# Should WiRe client get a GPS fix on startup?
1749 term.clear()
1750 term.setCursorPos(2, 1)
1751 term.write("Last question!")
1752 term.setCursorPos(2, 3)
1753 term.write("Do you want WiRe client to")
1754 term.setCursorPos(2, 4)
1755 term.write("aquire a GPS fix on startup? [y/n]")
1756 term.setCursorPos(2, 6)
1757 local getFix = string.lower(read())
1758 ccSettings.getGPSFix = getFix:sub(1, 1) == "y"
1759 if not fs.exists("/data") then fs.makeDir("/data") end
1760 saveData()
1761 return true
1762end
1763
1764term.setBackgroundColor(black)
1765if pocket or turtle then error("Computer REQUIRED.", 0) end
1766if not fs.exists(config) then
1767 if not firstRun() then return end
1768end
1769term.clear()
1770term.setCursorPos(2, 2)
1771term.setTextColor(white)
1772term.write("Ingesting configuration data . . .")
1773local clientConfig = fs.open(config, "r") or error("initMe(): Cannot open " .. config .. " for reading", 0)
1774ccSettings = textutils.unserialize(clientConfig.readAll())
1775clientConfig.close()
1776if ccSettings.getFix ~= nil or ccSettings.deviceType == "Bridge" or not ccSettings.newColors then
1777 if ccSettings.getFix ~= nil then
1778 ccSettings.getGPSFix = ccSettings.getFix
1779 ccSettings.getFix = nil
1780 end
1781 if ccSettings.deviceType == "Bridge" then
1782 ccSettings.deviceType = "Energy"
1783 end
1784 if not ccSettings.newColors then
1785 if ccSettings.color == "lgray" or ccSettings.color == "Light Gray" then
1786 ccSettings.color = "Silver"
1787 elseif ccSettings.color == "lblue" or ccSettings.color == "Light Blue" then
1788 ccSettings.color = "Sky"
1789 end
1790 ccSettings.newColors = true
1791 end
1792 saveData()
1793end
1794term.setCursorPos(2, 4)
1795term.write("Configuring hardware . . .")
1796for _, side in pairs(rs.getSides()) do
1797 if peripheral.isPresent(side) then
1798 if peripheral.getType(side) == "monitor" and peripheral.call(side, "isColor") then
1799 mon[#mon + 1] = peripheral.wrap(side)
1800 elseif peripheral.getType(side) == "modem" then
1801 if peripheral.call(side, "isWireless") then
1802 modemSide = side
1803 if not rednet.isOpen(side) then rednet.open(side) end
1804 else
1805 for _, perp in pairs(peripheral.call(side, "getNamesRemote")) do
1806 if peripheral.getType(perp) == "monitor" and peripheral.call(perp, "isColor") then
1807 mon[#mon + 1] = peripheral.wrap(perp)
1808 elseif peripheral.getType(perp) == "computer" then
1809 modemSide = side
1810 if not rednet.isOpen(side) then rednet.open(side) end
1811 end
1812 end
1813 end
1814 end
1815 end
1816end
1817if not modemSide then return initError("modem", "a modem") end
1818if mon[1] then
1819 for i = 1, #mon do
1820 mon[i].setTextScale(0.5)
1821 mon[i].setBackgroundColor(mwhite)
1822 mon[i].setTextColor(mblack)
1823 mon[i].clear()
1824 mon[i].setCursorPos(2, 3)
1825 mon[i].write("Initializing...")
1826 end
1827end
1828term.setCursorPos(2, 6)
1829if ccSettings.getGPSFix then
1830 term.write("Acquiring GPS fix . . .")
1831 loc.x, loc.y, loc.z = gps.locate(2)
1832 term.setCursorPos(2, 8)
1833end
1834if not loc.x then
1835 loc.x, loc.y, loc.z = "No GPS Fix", "No GPS Fix", "No GPS Fix"
1836end
1837term.write("Looking for WiRe Server . . .")
1838network = "WiRe" .. ccSettings.color
1839for i = 1, 3 do
1840 server = rednet.lookup(network, ccSettings.color)
1841 if server then break elseif i ~= 3 then sleep(2) end
1842end
1843if not server then
1844 term.clear()
1845 term.setTextColor(red)
1846 term.setCursorPos(1, 2)
1847 print("No server detected!")
1848 print("WiRe Client REQUIRES a WiRe Server.\n")
1849 print("Output set to 'Default Start'")
1850 if rednet.isOpen(modemSide) then rednet.close(modemSide) end
1851 if mon[1] then clearMonitors() end
1852 term.setCursorPos(1, 9)
1853end
1854deviceType = ccSettings.deviceType
1855onState = ccSettings.onState
1856offState = ccSettings.offState
1857defaultStart = ccSettings.defaultStart
1858autoClose = ccSettings.autoClose
1859autoDelay = ccSettings.autoDelay
1860local startState = false
1861if type(defaultStart) == "string" then
1862 startState = ccSettings.lastState
1863else
1864 startState = defaultStart
1865end
1866rs.setOutput(ccSettings.side, startState)
1867if not server then return end
1868if deviceType == "Door" then
1869 deviceState = startState and "OPEN" or "CLOSED"
1870elseif deviceType == "Piston" or deviceType == "eDoor" then
1871 deviceState = startState and "CLOSED" or "OPEN"
1872else
1873 deviceState = startState and "ON" or "OFF"
1874end
1875if autoClose and (deviceState == "OPEN" or deviceState == "OFF") then closeTimer = os.startTimer(autoDelay) end
1876netSend()
1877thisCommand = "init"
1878ccSuccess, kernelState = true, true
1879for i = 1, #mon do
1880 mon[i].setTextScale(1)
1881 mon[i].setBackgroundColor(mblack)
1882 mon[i].clear()
1883end
1884term.clear()
1885if not tArgs[1] then staticTermScreen() updateScreens() end
1886pollTimer = os.startTimer(5)
1887if tArgs[1] then
1888 parallel.waitForAny(netReceive, dataPoller, monTouch, foregroundShell)
1889else
1890 parallel.waitForAny(netReceive, dataPoller, monTouch, userInput)
1891end