--[[
   Sinelock v1.3
  The *COOLEST* computer/door lock ever!
 Now with slightly less seizure!

 pastebin get XDgeSDTq sinelock
 std pb XDgeSDTq sinelock
 std ld sinelock sinelock

 Now with salting!
--]]
local scr_x, scr_y = term.getSize()	--Gets screen size. Don't modify this

--Config variables start!
local terminateMode = 2				--0 enables termination, 1 blocks it, 2 provides a taunting screen.
local passFile = ".sl_password"		--The ABSOLUTE path of the password file.
local saltFile = ".sl_salt"			--The ABSOLUTE path of the salt file.
local characterLimit = 1024			--The cap of characters at the read() prompt. Set this to prevent crashes.
local runProgram = ""				--Set to something to run it when correct, and not using doors.
local dahChar = "*"					--The character used to hide characters when typed.
local doKeyCards = true				--Whether or not the lock also accepts keycards (floppy disks) as well as passwords.
local doEjectDisk = false			--Whether or not to eject a keycard after it's accepted, just to speed things up a bit.
local doorSides = {}				--If it has anything, the lock will open the doors instead of unlocking the PC.
local doInvertSignal = false		--If true, it will invert the redstone signal of the door, in case you need to.
local doShowDoorSides = true		--If true, will show the door sides being opened. Set to false if you are paranoid.
local beWavey = true				--Whether or not to animate the sine wave.
local readYpos = scr_y-2			--The Y position of the read() prompt
local requireAllPasswords = false	--Whether or not the lock asks for ONE of the multiple passwords, or ALL of them in order.
local badlength = 4					--The length in seconds that you have to wait to try again.
local goodlength = 6				--The length in seconds that doors will stay open.
local sineFrameDelay = 0.15			--The amount of time between sine animation frames. Tradeoff of purty vs performance.
local palate = {
	frontsine = colors.lightGray,
	backsine = colors.gray,
	background = colors.black,
	rainColor = colors.gray,
	rainChar = "|",
	promptBG = colors.gray,
	promptTXT = colors.white,
	wrongBG = colors.black,
	wrongTXT = colors.gray,
}
local language = "english"
local lang = {
	english = {
		wrong1 = "YOU ARE WRONG.",
		wrong2 = "YOU ARE WRONG. AS ALWAYS.",
		right1 = "Correct!",
		right2 = "Correct, finally!",
	},
	spanish = {
		wrong1 = "ESTA USTED EQUIVOCADO.",
		wrong2 = "ESTA USTED EQUIVOCADO. TODAVIA OTRA VEZ.",
		right1 = "Correcto!",
		right2 = "Asi es, por fin!",
		noTerminate = "No termine!",
	},
	german = {
		wrong1 = "SIE LIEGEN FALSCH.",
		wrong2 = "SIE LIEGEN FALSCH. WIE IMMER.",
		right1 = "Richtig!",
		right2 = "Richtig, endlich!",
		noTerminate = "Nicht zu beenden!",
	},
	dutch = {
		wrong1 = "U BENT ONJUIST.",
		wrong2 = "JE BENT ONJUIST, ALS ALTIJD.",
		right1 = "Dat is juist!",
		right2 = "Dat is juist, eindelijk!",
		noTerminate = "Niet te beeindigen!",
	},
	latin = { --As a joke
		wrong1 = "ERRAS",
		wrong2 = "TU DEFICIENTES!",
		right1 = "Quod suus 'verum!",
		right2 = "Quod suus 'verum, demum!",
		noTerminate = "Vade futuo te ipsum!",
	},
	italian = {
		wrong1 = "HAI SBAGLIATO.",
		wrong2 = "HAI SBAGLIATO, COME SEMPRE D'ALTRONDE.",
		right1 = "CORRETTO!",
		right2 = "CORRETTO, FINALMENTE!",
		noTerminate = "Non cercare di terminarmi",
	},
}

-- Config variables end. Don't touch anything else, m'kay?
if not _VERSION then
	return printError("Sorry, only CC 1.7 and later supported.")
end
local csv, salt, doSine
local floor, ceil, random, abs = math.floor, math.ceil, math.random, math.abs
local sin, cos = math.sin, math.cos
local rhite = term.write
local setTextColor, setBackgroundColor, getTextColor, getBackgroundColor = term.setTextColor, term.setBackgroundColor, term.getTextColor, term.getBackgroundColor
local setCursorPos, setCursorBlink, getCursorPos, getSize = term.setCursorPos, term.setCursorBlink, term.getCursorPos, term.getSize
local sineLevel = 1
local isTerminable = false
local kaykaycoolcool = true
if term.current().setVisible then
	csv = true
else
	csv = false
end

local writeError = function(...)
	local tx,bg = getTextColor(),getBackgroundColor()
	if term.isColor() then
		setTextColor(colors.red)
	else
		setTextColor(colors.white)
	end
	rhite(table.concat(arg," "))
	setTextColor(tx)
	setBackgroundColor(bg)
end

local goodPullEvent
if terminateMode == 1 then
	if os.pullEvent ~= os.pullEventRaw then
		goodPullEvent = os.pullEvent
	end
	os.pullEvent = os.pullEventRaw
end

local keepLooping = true

---- SHA256 START ----
--SHA256 implementation done by GravityScore.

local MOD = 2^32
local MODM = MOD-1

local function memoize(f)
	local mt = {}
	local t = setmetatable({}, mt)
	function mt:__index(k)
		local v = f(k)
		t[k] = v
		return v
	end
	return t
end

local function make_bitop_uncached(t, m)
	local function bitop(a, b)
		local res,p = 0,1
		while a ~= 0 and b ~= 0 do
			local am, bm = a % m, b % m
			res = res + t[am][bm] * p
			a = (a - am) / m
			b = (b - bm) / m
			p = p*m
		end
		res = res + (a + b) * p
		return res
	end
	return bitop
end

local function make_bitop(t)
	local op1 = make_bitop_uncached(t,2^1)
	local op2 = memoize(function(a) return memoize(function(b) return op1(a, b) end) end)
	return make_bitop_uncached(op2, 2 ^ (t.n or 1))
end

local bxor1 = make_bitop({[0] = {[0] = 0,[1] = 1}, [1] = {[0] = 1, [1] = 0}, n = 4})

local function bxor(a, b, c, ...)
	local z = nil
	if b then
		a = a % MOD
		b = b % MOD
		z = bxor1(a, b)
		if c then z = bxor(z, c, ...) end
		return z
	elseif a then return a % MOD
	else return 0 end
end

local function band(a, b, c, ...)
	local z
	if b then
		a = a % MOD
		b = b % MOD
		z = ((a + b) - bxor1(a,b)) / 2
		if c then z = bit32_band(z, c, ...) end
		return z
	elseif a then return a % MOD
	else return MODM end
end

local function bnot(x) return (-1 - x) % MOD end

local function rshift1(a, disp)
	if disp < 0 then return lshift(a,-disp) end
	return floor(a % 2 ^ 32 / 2 ^ disp)
end

local function rshift(x, disp)
	if disp > 31 or disp < -31 then return 0 end
	return rshift1(x % MOD, disp)
end

local function lshift(a, disp)
	if disp < 0 then return rshift(a,-disp) end 
	return (a * 2 ^ disp) % 2 ^ 32
end

local function rrotate(x, disp)
    x = x % MOD
    disp = disp % 32
    local low = band(x, 2 ^ disp - 1)
    return rshift(x, disp) + lshift(low, 32 - disp)
end

local k = {
	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
	0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
	0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
	0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
	0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
	0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
	0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
	0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
	0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
	0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
	0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
	0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
	0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
	0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
	0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
}

local function str2hexa(s)
	return (string.gsub(s, ".", function(c) return string.format("%02x", string.byte(c)) end))
end

local function num2s(l, n)
	local s = ""
	for i = 1, n do
		local rem = l % 256
		s = string.char(rem) .. s
		l = (l - rem) / 256
	end
	return s
end

local function s232num(s, i)
	local n = 0
	for i = i, i + 3 do n = n*256 + string.byte(s, i) end
	return n
end

local function preproc(msg, len)
	local extra = 64 - ((len + 9) % 64)
	len = num2s(8 * len, 8)
	msg = msg .. "\128" .. string.rep("\0", extra) .. len
	assert(#msg % 64 == 0)
	return msg
end

local function initH256(H)
	H[1] = 0x6a09e667
	H[2] = 0xbb67ae85
	H[3] = 0x3c6ef372
	H[4] = 0xa54ff53a
	H[5] = 0x510e527f
	H[6] = 0x9b05688c
	H[7] = 0x1f83d9ab
	H[8] = 0x5be0cd19
	return H
end

local function digestblock(msg, i, H)
	local w = {}
	for j = 1, 16 do w[j] = s232num(msg, i + (j - 1)*4) end
	for j = 17, 64 do
		local v = w[j - 15]
		local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3))
		v = w[j - 2]
		w[j] = w[j - 16] + s0 + w[j - 7] + bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10))
	end

	local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8]
	for i = 1, 64 do
		local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22))
		local maj = bxor(band(a, b), band(a, c), band(b, c))
		local t2 = s0 + maj
		local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25))
		local ch = bxor (band(e, f), band(bnot(e), g))
		local t1 = h + s1 + ch + k[i] + w[i]
		h, g, f, e, d, c, b, a = g, f, e, d + t1, c, b, a, t1 + t2
	end

	H[1] = band(H[1] + a)
	H[2] = band(H[2] + b)
	H[3] = band(H[3] + c)
	H[4] = band(H[4] + d)
	H[5] = band(H[5] + e)
	H[6] = band(H[6] + f)
	H[7] = band(H[7] + g)
	H[8] = band(H[8] + h)
end

local function sha256(...)
	local msg = table.concat(arg,",")
	msg = preproc(msg, #msg)
	local H = initH256({})
	for i = 1, #msg, 64 do digestblock(msg, i, H) end
	return str2hexa(num2s(H[1], 4) .. num2s(H[2], 4) .. num2s(H[3], 4) .. num2s(H[4], 4) ..
		num2s(H[5], 4) .. num2s(H[6], 4) .. num2s(H[7], 4) .. num2s(H[8], 4))
end

---- SHA256 END ----

local terminates = 0
local sillyTerminate = function()
	local goodpull = _G.os.pullEvent
	os.pullEvent = os.pullEventRaw
	terminates = terminates + 1
	local script = {
		"You shall not pass!",
		"THOU shalt not pass!",
		"Stop trying to pass!",
		"...maybe I'm not clear.",
		"YOU. SHALL NOT. PASS!!",
		"Pass thou shalt not!",
		"Hey, piss off, will ya?",
		"I haven't got all day.",
		"...no, shut up. I don't.",
		"I won't tell you it!",
		"No password for you.",
		"It's been hashed!",
		"Hashed...with a salt!",
		"I'll never tell you the salt.",
		"You know why?",
		"Because that requires passing!",
		"WHICH THOU SHALT NOT DO!",
		"(oh btw don't pass k?)",
		"You! Don't pass!",
		"That means YOU!",
		"Cut it out!",
		"Re: Cut it out!",
		"Perhaps if you keep it up...",
		"..hmm...",
		"..oh I give up.",
		"<bullshitterm>",
		"What? Is this what you wanted?",
		"Huh? No? I-it's not?",
		"Well then...!",
		"<toobad>",
		"YEAH I SAID IT",
		"You think you're a terminating machine!",
		"You think you're above consequence!",
		"...w-...",
		"eat my shorts",
		"Your attempts are futile anyhow.",
		"Here I am getting drunk off your sweat,",
		"...while you push CTRL and T.",
		"Or maybe you're pressing that [T] button.",
		"Like, um, in CCEmuRedux.",
		"But it matters not!",
		"For you see, my defences are great!",
		"Nothing can bust my rock-hard abs!",
		"<fuckinghell>",
		"Oh bualls.",
		"That was embarrasing.",
		"...?",
		"What're YOU lookin' at??",
		"You callin' me UNSTABLE!?",
		"HUH!??",
		"...",
		"...w-well at least I admit it",
		"...b-bakka",
		".......",
		"Hey, have you ever played EarthBound?",
		"(Yes. I'm gonna rant.)",
		"It's an RPG on the Super Nintendo.",
		"Like, instead of fighting fantasy demons,",
		"you fight stop signs and cars and shit",
		"And, like, you hit rabid dogs with a bat",
		"And you have magical PSI spells",
		"...speaking of PSI, I happen to use it a lot.",
		"...er, *I* as in the coder of this lock...",
		"And by PSI, I mean the mod.",
		"You don't see too many psychic locks these days.",
		"A shame, really.",
		"I bet a PSI lock could act as a heater with PSI Fire.",
		"Or maybe it can kill rats with PSI Ground or Thunder",
		"Maybe, you can get a psychic KEY lock, so, like,",
		"you could put it on that Psychonauts head door thing.",
		"...WHAT!? just a suggestion.",
		"Psychonauts is another game I reccommend.",
		"It has some really clever dialogue.",
		"I'm sure you'd like it quite a lot.",
		"I know, because you've been here for ten fucking minutes.",
		"And you're not ONE STEP closer to getting the password",
		"Which I've been guarding very, very closely.",
		"Yes. Extremely closely.",
		"Excrutiatingly, some would say.",
		"You know, I should probably get to shouting.",
		"*ahem*",
		"...",
		"*aahhhechhemmemmhmm*",
		"*aachkskacehchachkhackcoughfartfuckdammitaaucahkh*",
		"...",
		"STAHP IT",
		"STAHP TEHRMINATIN",
		"do it for the CHILDREN",
		"okay fuck the children, who needs 'em",
		"then",
		"um",
		"THINK OF THE...THE...",
		"the babies...?",
		"um",
		"<abuseofcommunity>",
		"That's a fucking horrible idea.",
		"I'd rather eat my own brain then think about that.",
		"I'd sooner kiss a pig!",
		"I'd sooner swallow an avocado pit!",
		"...",
		"You know, you suck so much.",
		"You suck so much, and I'm sick of writing this script",
		"If my knuckles bleed, you're paying my insurance",
		"In order to save time, money, and my joints,",
		"I believe it would be in order to...",
		"...to say,",
		"NO TERMINATING.",
	}
	setCursorBlink(false)
	local mess
	if terminates > #script then
		mess = script[#script]
	else
		mess = script[terminates]
	end
	if mess == "<bullshitterm>" then
		setBackgroundColor(colors.black)
		if term.isColor() then
			setTextColor(colors.yellow)
		else
			setTextColor(colors.white)
		end
		term.clear()
		setCursorPos(1,1)
		print(os.version())
		write("> ")
		setTextColor(colors.white)
		read()
		printError("shell:350: Unable to pass")
		sleep(2)
	elseif mess == "<toobad>" then
		setBackgroundColor(colors.black)
		if term.isColor() then
			setTextColor(colors.red)
		else
			setTextColor(colors.white)
		end
		term.clear()
		setCursorPos(1,scr_y/2)
		local toobad = " T"..("O"):rep(scr_x-8).." BAD!"
		for a = 1, #toobad do
			for y = 1, scr_y do
				setCursorPos(a,y)
				rhite(toobad:sub(a,a))
			end
			sleep(0)
		end
		sleep(1.5)
		for a = 1, 16 do
			if a%3 == 0 then
				setBackgroundColor(colors.white)
			elseif a%3 == 1 then
				setBackgroundColor(colors.black)
			else
				if term.isColor() then
					setBackgroundColor(colors.red)
				else
					setBackgroundColor(colors.gray)
				end
			end
			term.clear()
			sleep(0)
		end
	elseif mess == "<fuckinghell>" then
		writeError("Terminated")
		setBackgroundColor(colors.black)
		setTextColor(colors.white)
		term.blit(">","4","f")
		read()
		sleep(0.75)
		for a = 1, 2 do
			sleep(0.05)
			term.scroll(1)
		end
		for a = 1, scr_y do 
			sleep(0.05)
			term.scroll(-1)
		end
		sleep(0.25)
		setBackgroundColor(colors.gray)
		term.clear()
		sleep(0.1)
		setBackgroundColor(colors.lightGray)
		term.clear()
		sleep(0.1)
		setBackgroundColor(colors.white)
		term.clear()
		sleep(0.25)
		local msg = "taht didn't happan"
		term.setCursorPos(scr_x-#msg,scr_y)
		setTextColor(colors.black)
		rhite(msg)
		sleep(1)
	elseif mess == "<abuseofcommunity>" then
		setBackgroundColor(colors.white)
		setTextColor(colors.black)
		term.clear()
		setCursorPos(2,3)
		print("Since you're such a smart bastard, why don't you come up with something objectionable to think about?\n")
		setBackgroundColor(colors.gray)
		setTextColor(colors.white)
		term.clearLine()
		local yourFuckingShittyAssResponceThatSucksSoMuchBallsThatIWouldRatherListenToThatFuckingOwlFromOcarinaOfTimeBlatherAboutHisFuckingDayThanSitWithYouForAnotherGoddamnedSecondReeeeee = read()
		setBackgroundColor(colors.white)
		setTextColor(colors.black)
		for a = 1, 5 do
			sleep(0.6)
			write(".")
		end
		sleep(1)
		term.setTextColor(colors.red)
		for a = 1, 20 do
			sleep(0.05)
			write(".")
		end
		sleep(0.5)
	else
		setBackgroundColor(colors.gray)
		setTextColor(colors.white)
		setCursorPos(math.max(1,(scr_x/2)-(#mess/2)),scr_y/2)
		if language == "english" then
			write(mess)
		else
			write(lang[language].noTerminate)
		end
		sleep(1.5)
	end
	os.pullEvent = goodpull
	return terminates
end

local shuffle = function(txt)
	local output = ""
	for a = 1, #txt do
		if a % 2 == 0 then
			output = output..txt:sub(a,a)
		else
			output = txt:sub(a,a)..output
		end
	end
	return output
end

local goodpass = function(pswd,count)
	isTerminable = true
	doSine = false
	setCursorBlink(false)
	local flashes = {
		colors.white,
		colors.lightGray,
		colors.gray,
	}
	if type(pswd) == "table" then
		pswd = pswd[1]
	end
	setTextColor(colors.black)
	local correctmsg
	if count < 10 then
		correctmsg = lang[language].right1
	else
		correctmsg = lang[language].right2
	end
	for a = 1, #flashes do
		setBackgroundColor(flashes[#flashes-(a-1)])
		term.clear()
		setCursorPos((scr_x/2)-(#correctmsg/2),scr_y/2)
		rhite(correctmsg)
		sleep(0)
	end
	if #doorSides == 0 then
		sleep(0.4)
		keepLooping = false
	else
		local doormsg
		if doShowDoorSides then
			doormsg = "Applying RS to "..table.concat(doorSides,", ").."."
		else
			doormsg = "Applying redstone."
		end
		setCursorPos((scr_x/2)-(#doormsg/2),(scr_y/2)+2)
		rhite(doormsg)
		for a = 1, #doorSides do
			redstone.setOutput(doorSides[a],not doInvertSignal)
		end
		if terminateMode == 1 then
			os.pullEvent = goodPullEvent
		end
		sleep(goodlength)
		if terminateMode == 1 then
			os.pullEvent = os.pullEventRaw
		end
		for a = 1, #doorSides do
			redstone.setOutput(doorSides[a],doInvertSignal)
		end
	end
	for a = 1, #flashes do
		setBackgroundColor(flashes[a])
		term.clear()
		setCursorPos((scr_x/2)-(#correctmsg/2),scr_y/2)
		rhite(correctmsg)
		sleep(0)
	end
	setBackgroundColor(colors.black)
	term.clear()
	setCursorPos(1,1)
	if terminateMode == 1 and goodPullEvent and (#doorSides == 0) then
		os.pullEvent = goodPullEvent
	end
	setCursorBlink(true)
	isTerminable = false
	return true
end

local badpass = function(pswd,count)
	doSine = false
	local getevent = os.pullEvent
	os.pullEvent = os.pullEventRaw
	setCursorBlink(false)
	setBackgroundColor(palate.wrongBG)
	setTextColor(palate.wrongTXT)
	term.clear()
	if type(pswd) == "table" then
		pswd = pswd[1]
	end
	local badmsg
	if count < 10 then
		if pswd == sha256("bepis",salt) then
			badmsg = "Bepis."
		else
			badmsg = lang[language].wrong1
		end
	else
		if pswd == sha256("bepis",salt) then
			badmsg = "BEPIS!"
		else
			badmsg = lang[language].wrong2
		end
	end
	setCursorPos((scr_x/2)-(#badmsg/2),scr_y/2)
	rhite(badmsg)
	sleep(badlength)
	doSine = true
	setCursorBlink(true)
	os.pullEvent = getevent
	return "man you suck"
end
	
local readPassFile = function()
	local _output, _salt
	if fs.exists(passFile) then
		local file = fs.open(passFile,"r")
		_output = file.readLine()
		file.close()
	end
	if fs.exists(saltFile) then
		local file = fs.open(saltFile,"r")
		_salt = file.readLine()
		file.close()
	end
	return _output, _salt
end

local addNewPassword = function(pword,_path)
	local file = fs.open(_path or passFile,"a")
	file.write( sha256(pword,salt) )
	file.close()
end
       
local rendersine = function(move)
	move = move or 0
	local res1,res2,x,y
	if csv then term.current().setVisible(false) end
	setBackgroundColor(colors.black)
	setCursorBlink(false)
	for a = 1, scr_y do
		if a ~= readYpos then
			for b = 1, scr_x do
				x = b+floor(scr_x/2)
				y = a-floor(scr_y/2)
				res1 = abs( floor(sin((x/(scr_x/7.3))+move)*scr_y/4) - y ) <= 2
				setCursorPos(b,a)
				if res1 then
					setBackgroundColor(palate.backsine)
				else
					setBackgroundColor(palate.background)
				end
				rhite(" ")
				res2 = abs( floor(cos((x/(scr_x/12.75))+(move*4))*scr_y/7) - y+2 ) <= 1
				setCursorPos(b,a)
				if res2 then
					setBackgroundColor(palate.frontsine)
					rhite(" ")
				elseif not res1 then
					setBackgroundColor(palate.background)
					setTextColor(palate.rainColor)
					if (x % 2 == 0) and ((y+floor(move*-10)+(x % 5)) % 5 <= 1) then
						rhite(palate.rainChar)
					else
						rhite(" ")
					end
				end
			end
		end
	end
	if csv then term.current().setVisible(true) end
	setCursorBlink(true)
end

local sine = function()
	doSine = true
	while true do
		if sineLevel > 900 then
			sineLevel = 1
		end
		if doSine then
			local cX,cY = getCursorPos()
			local bg,txt = getBackgroundColor(),getTextColor()
			rendersine(sineLevel/10)
			setCursorPos(cX,cY)
			setBackgroundColor(bg)
			setTextColor(txt)
		end
		sleep(sineFrameDelay)
		if kaykaycoolcool then
			sineLevel = sineLevel + 1
		end
	end
end

local funcread = function(repchar,rHistory,doFunc,noNewLine,writeFunc,cursorAdjFunc,doFuncEvent,charLimit)
	local scr_x,scr_y = term.getSize()
	local sx,sy = term.getCursorPos()
	local cursor = 1
	local rCursor = #rHistory+1
	local output = ""
	term.setCursorBlink(true)
	local rite = writeFunc or term.write
	cursorAdjFunc = cursorAdjFunc or function() return 0 end
	while true do
		local evt,key = os.pullEvent()
		if evt == doFuncEvent then
			pleaseDoFunc = true
		elseif evt == "key" then
			if key == keys.enter then
				if not noNewLine then
					write("\n")
				end
				term.setCursorBlink(false)
				return output
			elseif key == keys.left then
				if cursor-1 >= 1 then
					cursor = cursor - 1
				end
			elseif key == keys.right then
				if cursor <= #output then
					cursor = cursor + 1
				end
			elseif key == keys.up then
				if rCursor > 1 then
					rCursor = rCursor - 1
					term.setCursorPos(sx,sy)
					rite((" "):rep(#output))
					output = (rHistory[rCursor] or ""):sub(1,charLimit or -1)
					cursor = #output+1
					pleaseDoFunc = true
				end
			elseif key == keys.down then
				term.setCursorPos(sx,sy)
				rite((" "):rep(#output))
				if rCursor < #rHistory then
					rCursor = rCursor + 1
					output = (rHistory[rCursor] or ""):sub(1,charLimit or -1)
					cursor = #output+1
					pleaseDoFunc = true
				else
					rCursor = #rHistory+1
					output = ""
					cursor = 1
				end
			elseif key == keys.backspace then
				if cursor > 1 and #output > 0 then
					output = (output:sub(1,cursor-2)..output:sub(cursor)):sub(1,charLimit or -1)
					cursor = cursor - 1
					pleaseDoFunc = true
				end
			elseif key == keys.delete then
				if #output:sub(cursor,cursor) == 1 then
					output = (output:sub(1,cursor-1)..output:sub(cursor+1)):sub(1,charLimit or -1)
					pleaseDoFunc = true
				end
			end
		elseif evt == "char" or evt == "paste" then
			output = (output:sub(1,cursor-1)..key..output:sub(cursor+(#key-1))):sub(1,charLimit or -1)
			cursor = math.min(#output+1,cursor+#key)
			pleaseDoFunc = true
		end
		local pOut = (output or ""):sub(math.max( 1,(#output+sx)-scr_x) )
		if pleaseDoFunc then
			pleaseDoFunc = false
			if type(doFunc) == "function" then
				doFunc(output:sub(1,charLimit or -1))
			end
			term.setCursorPos(sx,sy)
			if repchar then
				rite(repchar:sub(1,1):rep(#pOut))
			else
				rite(pOut)
			end
			term.write(" ")
		end
		term.setCursorPos(sx+cursorAdjFunc(pOut)+cursor-math.max( 1,(#output+sx)-scr_x),sy)
	end
end

local arse, arseSHA, passes

local awaitKeyCard = function()
	local bwop,_,side
	repeat
		_,side = os.pullEvent("disk")
		if side then
			bwop = fs.combine(disk.getMountPath(side),".sinepw") --bwop!
		else
			bwop = ""
		end
	until fs.exists(bwop) and not fs.isDir(bwop)
	local file = fs.open(bwop,"r")
	local output = file.readLine()
	file.close()
	arseSHA = output
	if doEjectDisk then disk.eject(side) end
end

local passwordPrompt = function()
	if requireAllPasswords then
		arse = {}
		arseSHA = ""
		for a = 1, ceil(#passes/64) do
			setCursorPos(1,readYpos)
			setBackgroundColor(palate.promptBG)
			term.clearLine()
			setTextColor(palate.promptTXT)
			write("P"..a..">")
			arse[#arse+1] = read(dahChar)
			arseSHA = arseSHA..sha256(arse[#arse],salt)
		end
	else
		setCursorPos(1,readYpos)
		setBackgroundColor(palate.promptBG)
		term.clearLine()
		setTextColor(palate.promptTXT)
		write(">")
		arse = funcread(dahChar,{},nil,true,nil,nil,nil,characterLimit)
		arseSHA = sha256(arse,salt)
	end
end

local count = 0

local lock = function()
	if #doorSides > 0 then
		for a = 1, #doorSides do
			redstone.setOutput(doorSides[a],doInvertSignal)
		end
	end
	while true do
		passes, salt = readPassFile()
		count = count + 1
		if doKeyCards then
			parallel.waitForAny(passwordPrompt,awaitKeyCard)
		else
			passwordPrompt()
		end
		local good
		if requireAllPasswords then
			if passes == arseSHA then
				good = true
			else
				good = false
			end
		else
			if string.find(passes,arseSHA) then
				good = true
			else
				good = false
			end
		end
		if good then
			goodpass(arseSHA,count)
			if #doorSides == 0 then
				return true
			else
				doSine = true
			end
		else
			badpass(arseSHA,count)
		end
	end
end

local choice = function(input)
	repeat
		event, key = os.pullEvent("key")
		if type(key) == "number" then key = keys.getName(key) end
		if key == nil then key = " " end
	until string.find(input, key)
	return key
end

if not fs.exists(saltFile) then
	local file = fs.open(saltFile,"w")
	for a = 1, 128 do
		local c = string.char(random(1,255))
		file.write(c)
	end
	file.close()
	local f = fs.open(saltFile,"r")
	salt = f.readLine()
	f.close()
end

passes, salt = readPassFile()

local tArg = {...}
if tArg[1] == "addpass" then
	if tArg[2] then
		print("Really add password? [Y,N]")
		local answer = choice("yn")
		if answer == "n" then
			sleep(0)
			return print("Oh, okay.")
		else
			table.remove(tArg,1)
			addNewPassword(table.concat(tArg," "))
			sleep(0)
			return print("Added password.")
		end
	else
		sleep(0)
		return print("Expected a new password...")
	end
elseif tArg[1] == "keymake" then
	if tArg[2] then
		print("Really add to disk?")
		print("Keep in mind the password has to be manually added for the keycard to work.\n[Y,N]")
		local answer = choice("yn")
		if answer == "n" then
			sleep(0)
			return print("Oh, okay.")
		else
			print("Please insert a disk or pocket computer.")
			local _,side = os.pullEvent("disk")
			local diskPassPath = fs.combine(disk.getMountPath(side),".sinepw")
			table.remove(tArg,1)
			addNewPassword(table.concat(tArg," "),diskPassPath)
			if not disk.getLabel(side) then
				disk.setLabel(side,"slkey-"..random(1,1000))
			end
			sleep(0)
			print("Added password.")
			if not doKeyCards then
				print("Key cards aren't enabled, though.")
			end
			return
		end
	else
		sleep(0)
		return print("Expected a password...")
	end
end

if not fs.exists(passFile) then
	local progname = fs.getName(shell.getRunningProgram())
	return print("No password file found.\nRun '"..progname.." addpass <password>' to add a password.")
end

setBackgroundColor(colors.black)
term.clear()

local parafunky = { --it looks kinda funky, but it tastes funKAY!
	sine,
	lock,
}

if not beWavey then table.remove(parafunky,1) end --not funky, man
local staytis, errawr
while keepLooping do
	kaykaycoolcool = true
	staytis, errawr = pcall(parallel.waitForAny,unpack(parafunky))
	if keepLooping == false then break else
		if terminateMode == 2 then
			kaykaycoolcool = false
			if not isTerminable then
				sillyTerminate()
			else
				keepLooping = false
				setBackgroundColor(colors.black)
				term.clear()
				setTextColor(colors.white)
				setCursorPos(1,1)
				break
			end
		else
			keepLooping = false
			setBackgroundColor(colors.black)
			term.clear()
			setTextColor(colors.white)
			setCursorPos(1,1)
			if not staytis then
				printError(errawr)
			end
			break
		end
	end
end
if runProgram and (runProgram ~= "") then
	shell.run(runProgram)
end