· 6 years ago · Oct 30, 2019, 09:20 PM
1-- Version 1.01
2
3-- ######################################################
4-- ## Table Utility Functions ##
5-- ######################################################
6
7function tableContainsValue(table, element)
8 for _, value in pairs(table) do
9 if value == element then
10 return true
11 end
12 end
13 return false
14end
15
16function tableContainsKey(table, element)
17 for key, value in pairs(table) do
18 if key == element then
19 return true
20 end
21 end
22 return false
23end
24
25
26function explode(div,str)
27 if (div=='') then return false end
28 local pos,arr = 0,{}
29 for st,sp in function() return string.find(str,div,pos,true) end do
30 table.insert(arr,string.sub(str,pos,st-1))
31 pos = sp + 1
32 end
33 table.insert(arr,string.sub(str,pos))
34 return arr
35end
36
37function dump(o)
38 if type(o) == 'table' then
39 local s = '{ '
40 for k,v in pairs(o) do
41 if type(k) ~= 'number' then k = '"'..k..'"' end
42 s = s .. '['..k..'] = ' .. dump(v) .. ','
43 end
44 return s .. '} '
45 else
46 return tostring(o)
47 end
48end
49
50function pairsByKeys (t, f)
51 local a = {}
52 for n in pairs(t) do
53 table.insert(a, n)
54 end
55 table.sort(a, f)
56 local i = 0 -- iterator variable
57 local iter = function () -- iterator function
58 i = i + 1
59 if a[i] == nil then
60 return nil
61 else
62 return a[i], t[a[i]]
63 end
64 end
65 return iter
66end
67
68function ripairs(t)
69 idx={}
70 for k,v in pairs(t) do
71 if type(k)=="number" then idx[#idx+1]=k end
72 end
73 table.sort(idx)
74 local function ripairs_it(t,_)
75 if #idx==0 then return nil end
76 k=idx[#idx]
77 idx[#idx]=nil
78 return k,t[k]
79 end
80 return ripairs_it, t, nil
81end
82
83function table_invert(t)
84 local s={}
85 for k,v in pairs(t) do
86 s[v]=k
87 end
88 return s
89end
90
91function table_count(T)
92 local count = 0
93 for _ in pairs(T) do count = count + 1 end
94 return count
95end
96
97--// The Save Function
98function table_save( tbl,filename )
99 local saveable = textutils.serialize(tbl)
100 local handle = assert(fs.open(filename, "w"), "Error saving to file.")
101 handle.write(saveable)
102 handle.close()
103end
104
105--// The Load Function
106function table_load( sfile )
107 local handle = assert(fs.open(sfile,"r"), "Error loading from file.")
108 local input = handle.readAll()
109 handle.close()
110 local settings = textutils.unserialize(input)
111 return settings
112end
113
114
115-- ######################################################
116-- ## String Functions ##
117-- ######################################################
118
119function padLeft(str, len, char)
120 if char == nil then char = ' ' end
121 if (str == nil) or (string.len(str) == 0) then
122 return string.rep(char, len)
123 else
124 if string.len(str) > len then
125 return string.sub(str,1,len)
126 else
127 return str .. string.rep(char, len - string.len(str))
128 end
129 end
130end
131
132function padRight(str, len, char)
133 if char == nil then char = ' ' end
134 if (str == nil) or (string.len(str) == 0) then
135 return string.rep(char, len)
136 else
137 if string.len(str) > len then
138 return string.sub(str,1,len)
139 else
140 return string.rep(char, len - string.len(str)) .. str
141 end
142 end
143end
144
145
146-- ######################################################
147-- ## "Encrpytion" Functions ##
148-- ######################################################
149-- From the ComputerCraft forums, mostly is obscuring strings
150-- rather than "true" encryption, but works for what we need
151-- which is not sending plaintext PW's over rednet.
152-- http://bit.ly/13zuMU9
153
154local function enc(str, key)
155 if not key then return false end
156 local f = ""
157 local kb = {}
158 for i = 1, #key do
159 kb[i] = string.byte(key:sub(i,i)) - 31
160 end
161 local c = 1
162 for i = 1, #str do
163 local t1 = string.byte(str:sub(i,i))
164 if t1 == 10 then
165 f = f .. string.char(10)
166 else
167 local t2 = t1 + kb[c]
168 if t2 > 126 then
169 local t3 = t2 - 126
170 t2 = 31 + t3
171 end
172 f = f .. string.char(t2)
173 end
174 c = c + 1
175 if c > #key then
176 c = 1
177 end
178 end
179 return f
180end
181
182local function dec(str, key)
183 if not key then return false end
184 local f = ""
185 local kb = {}
186 for i = 1, #key do
187 kb[i] = string.byte(key:sub(i,i)) - 31
188 end
189 local c = 1
190 for i = 1, #str do
191 local t2 = string.byte(str:sub(i,i)) - 31
192 if t2 == -21 then
193 f = f .. string.char(10)
194 else
195 local t3 = t2 - kb[c] + 31
196 if t3 < 32 then
197 t3 = t3 + 126 - 31
198 end
199 if t3 < 32 then return false, t3 end
200 if t3 > 126 then return false, t3 end
201 f = f .. string.char(t3)
202 end
203 c = c + 1
204 if c > #key then
205 c = 1
206 end
207 end
208 return f
209end
210
211function encrypt(str, key)
212 if not key then return false end
213 if #key < 3 then return false end
214 local f = str
215 key = key .. key .. "#me1"
216 for i = 0, #key - 1 do
217 f = enc(f, key:sub(i,i + 1))
218 end
219 f = enc(f, key:sub(1,2) .. key:sub(#key - 1, #key) .. enc(key, key .. key))
220 return f
221end
222
223function decrypt(str, key)
224 if not key then return false end
225 if #key < 3 then return false end
226 local f = str
227 key = key .. key .. "#me1"
228 for i = 0, #key - 1 do
229 f = dec(f, key:sub(i,i + 1))
230 end
231 f = dec(f, key:sub(1,2) .. key:sub(#key - 1, #key) .. enc(key, key .. key))
232 return f
233end
234
235
236-- ######################################################
237-- ## Terminal Functions ##
238-- ######################################################
239
240
241function setupScreen()
242 local newScreen = {}
243 for i = 4, 16 do
244 newScreen[i] = ""
245 end
246 return newScreen
247end
248
249function addLine(screen,newLine)
250 local newScreen = {}
251 for i = 4,15 do
252 newScreen[i] = screen[i+1]
253 end
254 newScreen[16] = newLine
255 return newScreen
256end
257
258function addPrintLine(screen,newLine)
259 local newScreen = {}
260 for i = 4,15 do
261 newScreen[i] = screen[i+1]
262 end
263 newScreen[16] = newLine
264 for i = 4,16 do
265 term.setCursorPos(1,i)
266 term.write("| "..padLeft(newScreen[i],47," ").." |")
267 end
268 return newScreen
269end
270
271function printScreen(screen)
272 for i = 4,16 do
273 term.setCursorPos(1,i)
274 print("| "..padLeft(screen[i],47," ").." |")
275 end
276end
277
278function justPrintLine(screen,newLine)
279 screen = addPrintLine(screen,newLine)
280 --printScreen(screen)
281 return screen
282end
283
284function printUILine(newLine)
285 term.setCursorPos(1,18)
286 term.write("| "..padLeft(newLine,47," ").." |")
287end
288
289
290-- ######################################################
291-- ## User Input Functions ##
292-- ######################################################
293
294
295function getInput(prompt)
296 printUILine(prompt)
297 term.setCursorPos(3+string.len(prompt)+1,18)
298 local input = read()
299 term.setCursorPos(1,1)
300 return input
301end
302
303function getConfigInput(prompt)
304 write(prompt)
305 local input = read()
306 return input
307end
308
309function getNumericInput(screen,prompt)
310 local input = getInput(prompt)
311 while (tonumber(input) == nil) do
312 screen = addPrintLine(screen,"Invalid entry, value must be numeric.")
313 printScreen(screen)
314 input = getInput(prompt)
315 end
316 return input
317end
318
319function getPasswordInput(prompt)
320 printUILine(prompt)
321 term.setCursorPos(3+string.len(prompt)+1,18)
322 local input = read("*")
323 term.setCursorPos(1,1)
324 return input
325end
326
327-- ######################################################
328-- ## Config Functions ##
329-- ######################################################
330
331function caseCheck(config,questionable)
332 if (config == nil) then return nil end
333 for option, settings in pairs(config) do
334 if (string.upper(questionable) == string.upper(option)) then
335 return option
336 end
337 end
338 return nil
339end
340
341function addConfigOption(config,named,default,prompt,optionType)
342 local t = {}
343 t["value"] = default
344 t["default"] = default
345 t["prompt"] = prompt
346 if (optionType ~= nil) then
347 t["type"] = optionType
348 else
349 t["type"] = 'normal'
350 end
351 config[named] = t
352 return config
353end
354
355function setConfigOption(config,named,newValue)
356 local t = config[caseCheck(config,named)]
357 t["value"] = newValue
358 config[named] = t
359 return config
360end
361
362function getConfigOption(config,named)
363 if (caseCheck(config,named) == nil) then return nil end
364 local t = config[caseCheck(config,named)]
365 local val = t["value"]
366 if (tonumber(val) ~= nil) then
367 -- it's a number, pass it back through tonumber just in case
368 val = tonumber(val)
369 return val
370 else
371 return val
372 end
373end
374
375function getConfigDefault(config,named)
376 local t = config[caseCheck(config,named)]
377 local val = t["default"]
378 if (tonumber(val) ~= nil) then
379 -- it's a number, pass it back through tonumber just in case
380 val = tonumber(val)
381 return val
382 else
383 return val
384 end
385end
386
387function getConfigPrompt(config,named)
388 local t = config[caseCheck(config,named)]
389 local val = t["prompt"]
390 if (val == nil) or (string.len(val) == 0) then
391 val = "X"
392 end
393 return val
394end
395
396function getValidateInput(screen,prompt,acceptable,default)
397 local input = ""
398 repeat
399 if (input ~= "") then
400 screen = addPrintLine(screen,"Invalid entry. Acceptable values are: ")
401 for i,v in ipairs(acceptable) do
402 if (string.lower(v)~= string.lower(default)) then
403 screen = addPrintLine(screen," "..v)
404 else
405 screen = addPrintLine(screen," *"..v)
406 end
407 end
408 printScreen(screen)
409 end
410 input = getInput(prompt)
411 until (tableContainsValue(acceptable,input) or (input == ""))
412 if (input == "") then
413 input = default
414 end
415 return input
416end
417
418function getConfigFromUser(screen,config,named)
419 local rightName = caseCheck(config,named)
420 local t = config[rightName]
421 local newValue = ''
422 if (t["type"] == 'normal') then
423 newValue = getInput(getConfigPrompt(config,rightName))
424 elseif (t["type"] == 'side') then
425 local sideValidation = redstone.getSides()
426 newValue = getValidateInput(screen,getConfigPrompt(config,rightName),redstone.getSides(),getConfigDefault(config,rightName))
427 elseif (t["type"] == 'pass') then
428 newValue = getPasswordInput(getConfigPrompt(config,rightName))
429 elseif (t["type"] == 'numeric') then
430 newValue = getNumericInput(screen,getConfigPrompt(config,rightName))
431 end
432 if (newValue == "") then
433 newValue = getConfigDefault(config,rightName)
434 end
435 config = setConfigOption(config,caseCheck(config,named),newValue)
436 return screen, config
437end
438
439function getConfigSideFromUser(screen,config,named)
440 local sideValidation = redstone.getSides()
441 local newValue = getValidateInput(screen,getConfigPrompt(config,caseCheck(config,named)),redstone.getSides(),getConfigDefault(config,caseCheck(config,named)))
442 config = setConfigOption(config,caseCheck(config,named),newValue)
443 return screen, config
444end
445
446function checkPassword(screen,config,named)
447 local t = config[caseCheck(config,named)]
448 if (t == nil) then
449 return true
450 end
451 if (t["type"] ~= 'pass') then -- is it a "password" type
452 return true -- if not, just return true
453 end
454
455 local rightPW = t["value"]
456 if (rightPW == nil) or (rightPW == "") then
457 return true
458 end
459
460 return (rightPW == getPasswordInput(t["prompt"]))
461end
462
463function verifyConfig(default,config)
464 -- verify all 'default' config options are in the new config table
465 for option,settings in pairs(default) do
466 local t = config[option]
467 if (t == nil) then
468 config[option] = settings
469 end
470 end
471 -- verify there are no deprecated config options
472 for option,settings in pairs(config) do
473 local t = default[option]
474 if (t == nil) then
475 config[option] = nil
476 end
477 end
478 return config
479end
480
481function printConfig(config,screen,which)
482 if (which == nil) then
483 for option,settings in pairsByKeys(config) do
484 --print(dump(settings))
485 --os.sleep(1.5)
486 if (settings["type"] ~= 'pass') then
487 screen = addLine(screen,padLeft(option,12," ")..": "..padLeft(settings["value"],36," "))
488 else
489 if (settings["value"] ~= nil) and (settings["value"] ~= "") then
490 screen = addLine(screen,padLeft(option,12," ")..": "..padLeft("",10,"*"))
491 else
492 screen = addLine(screen,padLeft(option,12," ")..": ".."<<not set>>")
493 end
494 end
495 end
496 else
497 which = caseCheck(config,which)
498 local settings = config[which]
499 if (settings["type"] ~= 'pass') then
500 screen = addLine(screen,padLeft(which,12," ")..": "..padLeft(settings["value"],36," "))
501 else
502 if (settings["value"] ~= nil) and (settings["value"] ~= "") then
503 screen = addLine(screen,padLeft(which,12," ")..": "..padLeft("",10,"*"))
504 else
505 screen = addLine(screen,padLeft(which,12," ")..": ".."<<not set>>")
506 end
507 end
508 end
509 printScreen(screen)
510 return screen
511end
512
513
514-- ######################################################
515-- ## Old Print Functions ##
516-- ######################################################
517
518function printLine(output)
519 -- just in case I need any other logic, using an API function
520 --print(output)
521 term.write(output)
522 local x, y = term.getCursorPos()
523 term.setCursorPos(1,y+1)
524end
525
526function changeUpdate(output)
527 oldX, oldY = term.getCursorPos()
528 term.setCursorPos(14,16)
529 write(padLeft(output,38," "))
530 term.setCursorPos(oldX,oldY)
531end
532
533function centerLine(output)
534 x, _ = term.getSize()
535 _, y = term.getCursorPos()
536 term.setCursorPos((x-#output)/2,y)
537 print(output)
538end
539
540function termClear(versionString)
541 term.clear()
542 x, _ = term.getSize()
543 term.setCursorPos(x-#tostring(os.getComputerID()),1)
544 write(tostring(os.getComputerID()))
545 centerLine("--==[ "..versionString.." ]==--")
546end
547
548function drawHeader(header3,header4,header5,header6,networkName,modemSide,col3,col4,col5,col6)
549 local divider = "---------------------------------------------------"
550 printLine(divider)
551 printLine(padLeft("Network",12," ").." "..padLeft("Modem",8," ")..padLeft(header3,8," ")..padLeft(header4,8," ")..padLeft(header5,8," ")..padLeft(header6,5," "))
552 printLine(divider)
553 printLine(padLeft(networkName,12," ").." "..padLeft(modemSide,8," ")..padLeft(col3,8," ")..padLeft(col4,8," ")..padLeft(col5,8," ")..padLeft(col6,5," "))
554 printLine(divider)
555end
556
557function printUpdateLine(content)
558 local oldX, oldY = term.getCursorPos()
559 term.setCursorPos(3,16)
560 print(padLeft(content,1,47))
561 term.setCursorPos(oldX,oldY)
562end
563
564-- ######################################################
565-- ## Terminal Templates ##
566-- ######################################################
567
568function printFrame()
569 term.clear()
570 term.setCursorPos(1,1)
571
572 printLine("+-------------------------------------------------+")
573 printLine("| |")
574 printLine("+-------------------------------------------------+")
575 for i = 4,16 do
576 printLine("| |")
577 end
578 printLine("+-------------------------------------------------+")
579 printLine("| |")
580 printLine("+-------------------------------------------------+")
581end
582
583function printMotorTemplate(config)
584 printFrame()
585 term.setCursorPos(3,2)
586 if (config ~= nil) and (table_count(config) > 0) then
587 write(padLeft("EC: "..getConfigOption(config,"Version").." | Network: "..getConfigOption(config,"Network"),47," "))
588 end
589end
590
591function printDoorTemplate(config)
592 printFrame()
593 term.setCursorPos(3,2)
594 if (config ~= nil) and (table_count(config) > 0) then
595 write(padLeft("EC: "..getConfigOption(config,"Version").." | Network: "..getConfigOption(config,"Network"),47," "))
596 end
597end
598
599function printCabinTemplate(config)
600 printFrame()
601 term.setCursorPos(3,2)
602 if (config ~= nil) and (table_count(config) > 0) then
603 write(padLeft(getConfigOption(config,"Intro"),47," "))
604 term.setCursorPos(3,4)
605 write(padLeft(getConfigOption(config,"Floor"),6," ").."| "..padLeft(getConfigOption(config,"Description"),36," "))
606 term.setCursorPos(1,5)
607 printLine("+-------------------------------------------------+")
608 end
609end
610
611-- ######################################################
612-- ## Communcation Functions ##
613-- ######################################################
614
615function openModem(whichSide)
616 whichSide = tostring(whichSide)
617 --print("Opening side: ["..whichSide.."]")
618 --BUGFIX 03/25/2013 - changed "nil" to "false"
619 if (rednet.isOpen(whichSide) == false) then
620 rednet.open(whichSide)
621 end
622end
623
624function broadcast(networkName,message)
625 message = networkName.."|"..message
626 --openModem(modemSide)
627 --print("Broadcasting: "..message)
628 rednet.broadcast(message)
629end
630
631function send(networkName,id,message)
632 message = networkName.."|"..message
633 --printLine("Sending ["..id.."]: "..message)
634 --openModem(modemSide)
635 rednet.send(id,message)
636end