· 6 years ago · Jan 14, 2020, 08:14 PM
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 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
1215end
1216
1217local 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)
1233end
1234
1235local 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
1283end
1284
1285do
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
1365end
1366
1367local 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) .. " ")
1381end
1382
1383local 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")
1398end
1399
1400do
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
1418end
1419
1420local function clearMonitors()
1421 for i = 1, #mon do
1422 mon[i].setBackgroundColor(mblack)
1423 mon[i].clear()
1424 end
1425end
1426
1427local function clearTerm()
1428 term.setBackgroundColor(black)
1429 term.setTextColor(white)
1430 term.clear()
1431 term.setCursorPos(1, 1)
1432end
1433
1434local 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
1440end
1441
1442local 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)
1455end
1456
1457local 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
1499end
1500
1501local 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
1523end
1524
1525local 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
1535end
1536
1537local 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)
1548end
1549
1550local 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 physical Door/Hatch? [d]")
1597 term.setCursorPos(2, 3)
1598 term.write("A Forcefield or Energy Door? [f]")
1599 term.setCursorPos(2, 4)
1600 term.write("A Piston door or Piston hatch? [p]")
1601 term.setCursorPos(2, 5)
1602 term.write("Energy Device/Barrier/Lamp/Other? [e]")
1603 term.setCursorPos(2, 7)
1604 term.write("[d/f/p] - States are 'OPEN/CLOSED'")
1605 term.setCursorPos(2, 9)
1606 term.write("Door: OPEN = RS(true) / CLOSED = RS(false)")
1607 term.setCursorPos(2, 10)
1608 term.write("eDoor: OPEN = RS(false) / CLOSED = RS(true)")
1609 term.setCursorPos(2, 11)
1610 term.write("Piston: OPEN = RS(false) / CLOSED = RS(true)")
1611 term.setCursorPos(2, 13)
1612 term.write("[e] - States are 'OFF/ON'")
1613 term.setCursorPos(2, 15)
1614 term.write("Energy: OFF = RS(false) / ON = RS(true)")
1615 repeat
1616 term.setCursorPos(2, 17)
1617 local newType = string.lower(read())
1618 if deviceTypes[newType] then
1619 deviceType = deviceTypes[newType][1]
1620 onState = deviceTypes[newType][2]
1621 offState = deviceTypes[newType][3]
1622 ccSettings.onState = onState
1623 ccSettings.offState = offState
1624 ccSettings.deviceType = deviceType
1625 end
1626 until deviceTypes[newType]
1627 --# Select auto-close setting
1628 term.clear()
1629 term.setCursorPos(2, 2)
1630 if deviceType == "Door" or deviceType == "eDoor" or deviceType == "Piston" then
1631 term.write("Would you like the door or piston to")
1632 term.setCursorPos(2, 3)
1633 term.write("automatically close shortly after")
1634 term.setCursorPos(2, 4)
1635 term.write("being opened? [y/n]")
1636 else
1637 term.write("Would you like the device to")
1638 term.setCursorPos(2, 3)
1639 term.write("automatically re-activate shortly")
1640 term.setCursorPos(2, 4)
1641 term.write("after being deactivated? [y/n]")
1642 end
1643 term.setCursorPos(2, 6)
1644 local closeRule = string.lower(read())
1645 autoClose = closeRule:sub(1, 1) == "y"
1646 ccSettings.autoClose = autoClose
1647 --# Select auto-close delay
1648 if autoClose then
1649 term.clear()
1650 term.setCursorPos(2, 2)
1651 term.write("How many seconds should the")
1652 term.setCursorPos(2, 3)
1653 if deviceType == "Door" or deviceType == "eDoor" or deviceType == "Piston" then
1654 term.write("door or piston stay open before closing")
1655 else
1656 term.write("device stay off before reactivating")
1657 end
1658 term.setCursorPos(2, 4)
1659 term.write("automatically?")
1660 repeat
1661 term.setCursorPos(2, 6)
1662 local closeTime = tonumber(read())
1663 if closeTime then
1664 autoDelay = closeTime
1665 ccSettings.autoDelay = autoDelay
1666 end
1667 until closeTime
1668 end
1669 --# Select default startup state
1670 term.clear()
1671 term.setCursorPos(2, 1)
1672 term.write("What would you like the default")
1673 term.setCursorPos(2, 2)
1674 term.write("STARTUP state to be?")
1675 term.setCursorPos(2, 4)
1676 if deviceType == "Door" or deviceType == "eDoor" or deviceType == "Piston" then
1677 term.write("[p] Previous State or [c] CLOSED or [o] OPEN")
1678 else
1679 term.write("[p] Previous State or [n] ON or [f] OFF")
1680 end
1681 while true do
1682 term.setCursorPos(2, 5)
1683 local newStart = string.lower(read())
1684 if newStart == "o" or newStart == "c" or newStart == "n" or newStart == "f" or newStart == "p" then
1685 if newStart == "c" or newStart == "n" then
1686 defaultStart = onState
1687 elseif newStart == "o" or newStart == "f" then
1688 defaultStart = offState
1689 elseif newStart == "p" then
1690 defaultStart = "last"
1691 ccSettings.lastState = false
1692 end
1693 ccSettings.defaultStart = defaultStart
1694 break
1695 end
1696 end
1697 --# Select color
1698 local colorWheel = {
1699 P = "Purple";
1700 M = "Magenta";
1701 B = "Blue";
1702 S = "Sky";
1703 C = "Cyan";
1704 E = "Green";
1705 L = "Lime";
1706 R = "Red";
1707 O = "Orange";
1708 Y = "Yellow";
1709 N = "Brown";
1710 K = "Black";
1711 G = "Gray";
1712 I = "Silver";
1713 W = "White";
1714 }
1715 term.clear()
1716 term.setCursorPos(2, 1)
1717 term.write("Please select the network group")
1718 term.setCursorPos(2, 2)
1719 if deviceType == "Door" or device == "eDoor" then
1720 term.write("this door will belong to...")
1721 else
1722 term.write("this device will belong to...")
1723 end
1724 local ttY = 4
1725 for k, v in pairs(colorWheel) do
1726 term.setCursorPos(2, ttY)
1727 term.write(k .. " = " .. v)
1728 ttY = ttY + 1
1729 end
1730 repeat
1731 term.setCursorPos(32, 2)
1732 local newColor = string.upper(read())
1733 ccSettings.color = colorWheel[newColor:sub(1, 1)]
1734 until colorWheel[newColor:sub(1, 1)]
1735 --# Select redstone output side
1736 local theseSides = { "top", "bottom", "left", "right", "front", "back" }
1737 term.clear()
1738 term.setCursorPos(2, 2)
1739 term.write("Select the redstone output side")
1740 local yPos = 3
1741 for num, side in pairs(theseSides) do
1742 yPos = yPos + 1
1743 term.setCursorPos(2, yPos)
1744 term.write(tostring(num) .. " = " .. string.upper(side:sub(1, 1)) .. side:sub(2))
1745 end
1746 repeat
1747 term.setCursorPos(2, 11)
1748 local rsSide = tonumber(read())
1749 ccSettings.side = theseSides[rsSide]
1750 until theseSides[rsSide]
1751 --# Should WiRe client get a GPS fix on startup?
1752 term.clear()
1753 term.setCursorPos(2, 1)
1754 term.write("Last question!")
1755 term.setCursorPos(2, 3)
1756 term.write("Do you want WiRe client to")
1757 term.setCursorPos(2, 4)
1758 term.write("aquire a GPS fix on startup? [y/n]")
1759 term.setCursorPos(2, 6)
1760 local getFix = string.lower(read())
1761 ccSettings.getGPSFix = getFix:sub(1, 1) == "y"
1762 if not fs.exists("/data") then fs.makeDir("/data") end
1763 saveData()
1764 return true
1765end
1766
1767term.setBackgroundColor(black)
1768if pocket or turtle then error("Computer REQUIRED.", 0) end
1769if not fs.exists(config) then
1770 if not firstRun() then return end
1771end
1772term.clear()
1773term.setCursorPos(2, 2)
1774term.setTextColor(white)
1775term.write("Ingesting configuration data . . .")
1776local clientConfig = fs.open(config, "r") or error("initMe(): Cannot open " .. config .. " for reading", 0)
1777ccSettings = textutils.unserialize(clientConfig.readAll())
1778clientConfig.close()
1779if ccSettings.getFix ~= nil or ccSettings.deviceType == "Bridge" or not ccSettings.newColors then
1780 if ccSettings.getFix ~= nil then
1781 ccSettings.getGPSFix = ccSettings.getFix
1782 ccSettings.getFix = nil
1783 end
1784 if ccSettings.deviceType == "Bridge" then
1785 ccSettings.deviceType = "Energy"
1786 end
1787 if not ccSettings.newColors then
1788 if ccSettings.color == "lgray" or ccSettings.color == "Light Gray" then
1789 ccSettings.color = "Silver"
1790 elseif ccSettings.color == "lblue" or ccSettings.color == "Light Blue" then
1791 ccSettings.color = "Sky"
1792 end
1793 ccSettings.newColors = true
1794 end
1795 saveData()
1796end
1797term.setCursorPos(2, 4)
1798term.write("Configuring hardware . . .")
1799for _, side in pairs(rs.getSides()) do
1800 if peripheral.isPresent(side) then
1801 if peripheral.getType(side) == "monitor" and peripheral.call(side, "isColor") then
1802 mon[#mon + 1] = peripheral.wrap(side)
1803 elseif peripheral.getType(side) == "modem" then
1804 if peripheral.call(side, "isWireless") then
1805 modemSide = side
1806 if not rednet.isOpen(side) then rednet.open(side) end
1807 else
1808 for _, perp in pairs(peripheral.call(side, "getNamesRemote")) do
1809 if peripheral.getType(perp) == "monitor" and peripheral.call(perp, "isColor") then
1810 mon[#mon + 1] = peripheral.wrap(perp)
1811 elseif peripheral.getType(perp) == "computer" then
1812 modemSide = side
1813 if not rednet.isOpen(side) then rednet.open(side) end
1814 end
1815 end
1816 end
1817 end
1818 end
1819end
1820if not modemSide then return initError("modem", "a modem") end
1821if mon[1] then
1822 for i = 1, #mon do
1823 mon[i].setTextScale(0.5)
1824 mon[i].setBackgroundColor(mwhite)
1825 mon[i].setTextColor(mblack)
1826 mon[i].clear()
1827 mon[i].setCursorPos(2, 3)
1828 mon[i].write("Initializing...")
1829 end
1830end
1831term.setCursorPos(2, 6)
1832if ccSettings.getGPSFix then
1833 term.write("Acquiring GPS fix . . .")
1834 loc.x, loc.y, loc.z = gps.locate(2)
1835 term.setCursorPos(2, 8)
1836end
1837if not loc.x then
1838 loc.x, loc.y, loc.z = "No GPS Fix", "No GPS Fix", "No GPS Fix"
1839end
1840term.write("Looking for WiRe Server . . .")
1841network = "WiRe" .. ccSettings.color
1842for i = 1, 3 do
1843 server = rednet.lookup(network, ccSettings.color)
1844 if server then break elseif i ~= 3 then sleep(2) end
1845end
1846if not server then
1847 term.clear()
1848 term.setTextColor(red)
1849 term.setCursorPos(1, 2)
1850 print("No server detected!")
1851 print("WiRe Client REQUIRES a WiRe Server.\n")
1852 print("Output set to 'Default Start'")
1853 if rednet.isOpen(modemSide) then rednet.close(modemSide) end
1854 if mon[1] then clearMonitors() end
1855 term.setCursorPos(1, 9)
1856end
1857deviceType = ccSettings.deviceType
1858onState = ccSettings.onState
1859offState = ccSettings.offState
1860defaultStart = ccSettings.defaultStart
1861autoClose = ccSettings.autoClose
1862autoDelay = ccSettings.autoDelay
1863local startState = false
1864if type(defaultStart) == "string" then
1865 startState = ccSettings.lastState
1866else
1867 startState = defaultStart
1868end
1869rs.setOutput(ccSettings.side, startState)
1870if not server then return end
1871if deviceType == "Door" then
1872 deviceState = startState and "OPEN" or "CLOSED"
1873elseif deviceType == "Piston" or deviceType == "eDoor" then
1874 deviceState = startState and "CLOSED" or "OPEN"
1875else
1876 deviceState = startState and "ON" or "OFF"
1877end
1878if autoClose and (deviceState == "OPEN" or deviceState == "OFF") then closeTimer = os.startTimer(autoDelay) end
1879netSend()
1880thisCommand = "init"
1881ccSuccess, kernelState = true, true
1882for i = 1, #mon do
1883 mon[i].setTextScale(1)
1884 mon[i].setBackgroundColor(mblack)
1885 mon[i].clear()
1886end
1887term.clear()
1888if not tArgs[1] then staticTermScreen() updateScreens() end
1889pollTimer = os.startTimer(5)
1890if tArgs[1] then
1891 parallel.waitForAny(netReceive, dataPoller, monTouch, foregroundShell)
1892else
1893 parallel.waitForAny(netReceive, dataPoller, monTouch, userInput)
1894end