local HttpService = game:GetService("HttpService")
local utilsrc = HttpService:GetAsync("https://raw.githubusercontent.com/scandaloux/util/main/util.luau")
local _ = loadstring(utilsrc)
_ = _()
local NLS = getfenv().NLS
local owner = getfenv().owner or game.Players:WaitForChild("Synarx")
type RoactBinding = {
map: (RoactBinding, (any) -> nil) -> {any}
}
type Roact = {
createElement: (string, {[string]: any}?, {[string]: {any}}?) -> {any},
mount: ({any}, Instance) -> Instance,
createFragment: ({any}) -> {any},
Component: {extend: ({any}, string) -> {any}},
createBinding: (any) -> (RoactBinding, (any) -> nil),
joinBindings: ({RoactBinding}) -> RoactBinding,
createRef: () -> {any},
Ref: any,
}
local Roact: Roact = _.Roact
local base64 = _.base64
local LibDeflate = _.LibDeflate
script.Name = "Terrain"
local linux = Roact.Component:extend("archlinux") do
local FULLSCALE = UDim2.fromScale(1, 1)
local fiu = (function() -- https://raw.githubusercontent.com/rce-incorporated/Fiu/main/Source.lua
-- // Environment changes in the VM are not supposed to alter the behaviour of the VM so we localise globals beforehand
local type = type
local pcall = pcall
local error = error
local tonumber = tonumber
local assert = assert
local setmetatable = setmetatable
local string_format = string.format
local table_move = table.move
local table_pack = table.pack
local table_unpack = table.unpack
local table_create = table.create
local table_insert = table.insert
local table_remove = table.remove
local coroutine_create = coroutine.create
local coroutine_yield = coroutine.yield
local coroutine_resume = coroutine.resume
local coroutine_close = coroutine.close
local buffer_fromstring = buffer.fromstring
local buffer_len = buffer.len
local buffer_readu8 = buffer.readu8
local buffer_readu32 = buffer.readu32
local buffer_readstring = buffer.readstring
local buffer_readf32 = buffer.readf32
local buffer_readf64 = buffer.readf64
local bit32_bor = bit32.bor
local bit32_band = bit32.band
local bit32_btest = bit32.btest
local bit32_rshift = bit32.rshift
local bit32_lshift = bit32.lshift
local bit32_extract = bit32.extract
local ttisnumber = function(v) return type(v) == "number" end
local ttisstring = function(v) return type(v) == "string" end
local ttisboolean = function(v) return type(v) == "boolean" end
local ttisfunction = function(v) return type(v) == "function" end
-- // opList contains information about the instruction, each instruction is defined in this format:
-- // {OP_NAME, OP_MODE, K_MODE, HAS_AUX}
-- // OP_MODE specifies what type of registers the instruction uses if any
-- 0 = NONE
-- 1 = A
-- 2 = AB
-- 3 = ABC
-- 4 = AD
-- 5 = AE
-- // K_MODE specifies if the instruction has a register that holds a constant table index, which will be directly converted to the constant in the 2nd pass
-- 0 = NONE
-- 1 = AUX
-- 2 = C
-- 3 = D
-- 4 = AUX import
-- 5 = AUX boolean low 1 bit
-- 6 = AUX number low 24 bits
-- // HAS_AUX boolean specifies whether the instruction is followed up with an AUX word, which may be used to execute the instruction.
local opList = {
{ "NOP", 0, 0, false },
{ "BREAK", 0, 0, false },
{ "LOADNIL", 1, 0, false },
{ "LOADB", 3, 0, false },
{ "LOADN", 4, 0, false },
{ "LOADK", 4, 3, false },
{ "MOVE", 2, 0, false },
{ "GETGLOBAL", 1, 1, true },
{ "SETGLOBAL", 1, 1, true },
{ "GETUPVAL", 2, 0, false },
{ "SETUPVAL", 2, 0, false },
{ "CLOSEUPVALS", 1, 0, false },
{ "GETIMPORT", 4, 4, true },
{ "GETTABLE", 3, 0, false },
{ "SETTABLE", 3, 0, false },
{ "GETTABLEKS", 3, 1, true },
{ "SETTABLEKS", 3, 1, true },
{ "GETTABLEN", 3, 0, false },
{ "SETTABLEN", 3, 0, false },
{ "NEWCLOSURE", 4, 0, false },
{ "NAMECALL", 3, 1, true },
{ "CALL", 3, 0, false },
{ "RETURN", 2, 0, false },
{ "JUMP", 4, 0, false },
{ "JUMPBACK", 4, 0, false },
{ "JUMPIF", 4, 0, false },
{ "JUMPIFNOT", 4, 0, false },
{ "JUMPIFEQ", 4, 0, true },
{ "JUMPIFLE", 4, 0, true },
{ "JUMPIFLT", 4, 0, true },
{ "JUMPIFNOTEQ", 4, 0, true },
{ "JUMPIFNOTLE", 4, 0, true },
{ "JUMPIFNOTLT", 4, 0, true },
{ "ADD", 3, 0, false },
{ "SUB", 3, 0, false },
{ "MUL", 3, 0, false },
{ "DIV", 3, 0, false },
{ "MOD", 3, 0, false },
{ "POW", 3, 0, false },
{ "ADDK", 3, 2, false },
{ "SUBK", 3, 2, false },
{ "MULK", 3, 2, false },
{ "DIVK", 3, 2, false },
{ "MODK", 3, 2, false },
{ "POWK", 3, 2, false },
{ "AND", 3, 0, false },
{ "OR", 3, 0, false },
{ "ANDK", 3, 2, false },
{ "ORK", 3, 2, false },
{ "CONCAT", 3, 0, false },
{ "NOT", 2, 0, false },
{ "MINUS", 2, 0, false },
{ "LENGTH", 2, 0, false },
{ "NEWTABLE", 2, 0, true },
{ "DUPTABLE", 4, 3, false },
{ "SETLIST", 3, 0, true },
{ "FORNPREP", 4, 0, false },
{ "FORNLOOP", 4, 0, false },
{ "FORGLOOP", 4, 8, true },
{ "FORGPREP_INEXT", 4, 0, false },
{ "FASTCALL3", 3, 1, true },
{ "FORGPREP_NEXT", 4, 0, false },
{ "DEP_FORGLOOP_NEXT", 0, 0, false },
{ "GETVARARGS", 2, 0, false },
{ "DUPCLOSURE", 4, 3, false },
{ "PREPVARARGS", 1, 0, false },
{ "LOADKX", 1, 1, true },
{ "JUMPX", 5, 0, false },
{ "FASTCALL", 3, 0, false },
{ "COVERAGE", 5, 0, false },
{ "CAPTURE", 2, 0, false },
{ "SUBRK", 3, 7, false },
{ "DIVRK", 3, 7, false },
{ "FASTCALL1", 3, 0, false },
{ "FASTCALL2", 3, 0, true },
{ "FASTCALL2K", 3, 1, true },
{ "FORGPREP", 4, 0, false },
{ "JUMPXEQKNIL", 4, 5, true },
{ "JUMPXEQKB", 4, 5, true },
{ "JUMPXEQKN", 4, 6, true },
{ "JUMPXEQKS", 4, 6, true },
{ "IDIV", 3, 0, false },
{ "IDIVK", 3, 2, false },
}
local LUA_MULTRET = -1
local LUA_GENERALIZED_TERMINATOR = -2
local function luau_newsettings()
return {
vectorCtor = function() error("vectorCtor was not provided") end,
vectorSize = 4,
useNativeNamecall = false,
namecallHandler = function() error("Native __namecall handler was not provided") end,
extensions = {},
callHooks = {},
errorHandling = true,
generalizedIteration = true,
allowProxyErrors = false,
useImportConstants = false,
staticEnvironment = {},
decodeOp = function(op) return op end
}
end
local function luau_validatesettings(luau_settings)
assert(type(luau_settings) == "table", "luau_settings should be a table")
assert(type(luau_settings.vectorCtor) == "function", "luau_settings.vectorCtor should be a function")
assert(type(luau_settings.vectorSize) == "number", "luau_settings.vectorSize should be a number")
assert(type(luau_settings.useNativeNamecall) == "boolean", "luau_settings.useNativeNamecall should be a boolean")
assert(type(luau_settings.namecallHandler) == "function", "luau_settings.namecallHandler should be a function")
assert(type(luau_settings.extensions) == "table", "luau_settings.extensions should be a table of functions")
assert(type(luau_settings.callHooks) == "table", "luau_settings.callHooks should be a table of functions")
assert(type(luau_settings.errorHandling) == "boolean", "luau_settings.errorHandling should be a boolean")
assert(type(luau_settings.generalizedIteration) == "boolean", "luau_settings.generalizedIteration should be a boolean")
assert(type(luau_settings.allowProxyErrors) == "boolean", "luau_settings.allowProxyErrors should be a boolean")
assert(type(luau_settings.staticEnvironment) == "table", "luau_settings.staticEnvironment should be a table")
assert(type(luau_settings.useImportConstants) == "boolean", "luau_settings.useImportConstants should be a boolean")
assert(type(luau_settings.decodeOp) == "function", "luau_settings.function should be a function")
end
local function resolveImportConstant(static, count, k0, k1, k2)
local res = static[k0]
if count < 2 or res == nil then
return res
end
res = res[k1]
if count < 3 or res == nil then
return res
end
res = res[k2]
return res
end
local function luau_deserialize(bytecode, luau_settings)
if luau_settings == nil then
luau_settings = luau_newsettings()
else
luau_validatesettings(luau_settings)
end
local stream = if type(bytecode) == "string" then buffer_fromstring(bytecode) else bytecode
local cursor = 0
local function readByte()
local byte = buffer_readu8(stream, cursor)
cursor = cursor + 1
return byte
end
local function readWord()
local word = buffer_readu32(stream, cursor)
cursor = cursor + 4
return word
end
local function readFloat()
local float = buffer_readf32(stream, cursor)
cursor = cursor + 4
return float
end
local function readDouble()
local double = buffer_readf64(stream, cursor)
cursor = cursor + 8
return double
end
local function readVarInt()
local result = 0
for i = 0, 4 do
local value = readByte()
result = bit32_bor(result, bit32_lshift(bit32_band(value, 0x7F), i * 7))
if not bit32_btest(value, 0x80) then
break
end
end
return result
end
local function readString()
local size = readVarInt()
if size == 0 then
return ""
else
local str = buffer_readstring(stream, cursor, size)
cursor = cursor + size
return str
end
end
local luauVersion = readByte()
local typesVersion = 0
if luauVersion == 0 then
error("the provided bytecode is an error message",0)
elseif luauVersion < 3 or luauVersion > 6 then
error("the version of the provided bytecode is unsupported",0)
elseif luauVersion >= 4 then
typesVersion = readByte()
end
local stringCount = readVarInt()
local stringList = table_create(stringCount)
for i = 1, stringCount do
stringList[i] = readString()
end
local function readInstruction(codeList)
local value = luau_settings.decodeOp(readWord())
local opcode = bit32_band(value, 0xFF)
local opinfo = opList[opcode + 1]
local opname = opinfo[1]
local opmode = opinfo[2]
local kmode = opinfo[3]
local usesAux = opinfo[4]
local inst = {
opcode = opcode;
opname = opname;
opmode = opmode;
kmode = kmode;
usesAux = usesAux;
}
table_insert(codeList, inst)
if opmode == 1 then --[[ A ]]
inst.A = bit32_band(bit32_rshift(value, 8), 0xFF)
elseif opmode == 2 then --[[ AB ]]
inst.A = bit32_band(bit32_rshift(value, 8), 0xFF)
inst.B = bit32_band(bit32_rshift(value, 16), 0xFF)
elseif opmode == 3 then --[[ ABC ]]
inst.A = bit32_band(bit32_rshift(value, 8), 0xFF)
inst.B = bit32_band(bit32_rshift(value, 16), 0xFF)
inst.C = bit32_band(bit32_rshift(value, 24), 0xFF)
elseif opmode == 4 then --[[ AD ]]
inst.A = bit32_band(bit32_rshift(value, 8), 0xFF)
local temp = bit32_band(bit32_rshift(value, 16), 0xFFFF)
inst.D = if temp < 0x8000 then temp else temp - 0x10000
elseif opmode == 5 then --[[ AE ]]
local temp = bit32_band(bit32_rshift(value, 8), 0xFFFFFF)
inst.E = if temp < 0x800000 then temp else temp - 0x1000000
end
if usesAux then
local aux = readWord()
inst.aux = aux
table_insert(codeList, {value = aux, opname = "auxvalue" })
end
return usesAux
end
local function checkkmode(inst, k)
local kmode = inst.kmode
if kmode == 1 then --// AUX
inst.K = k[inst.aux + 1]
elseif kmode == 2 then --// C
inst.K = k[inst.C + 1]
elseif kmode == 3 then--// D
inst.K = k[inst.D + 1]
elseif kmode == 4 then --// AUX import
local extend = inst.aux
local count = bit32_rshift(extend, 30)
local id0 = bit32_band(bit32_rshift(extend, 20), 0x3FF)
inst.K0 = k[id0 + 1]
inst.KC = count
if count == 2 then
local id1 = bit32_band(bit32_rshift(extend, 10), 0x3FF)
inst.K1 = k[id1 + 1]
elseif count == 3 then
local id1 = bit32_band(bit32_rshift(extend, 10), 0x3FF)
local id2 = bit32_band(bit32_rshift(extend, 0), 0x3FF)
inst.K1 = k[id1 + 1]
inst.K2 = k[id2 + 1]
end
if luau_settings.useImportConstants then
inst.K = resolveImportConstant(
luau_settings.staticEnvironment,
count, inst.K0, inst.K1, inst.K2
)
end
elseif kmode == 5 then --// AUX boolean low 1 bit
inst.K = bit32_extract(inst.aux, 0, 1) == 1
inst.KN = bit32_extract(inst.aux, 31, 1) == 1
elseif kmode == 6 then --// AUX number low 24 bits
inst.K = k[bit32_extract(inst.aux, 0, 24) + 1]
inst.KN = bit32_extract(inst.aux, 31, 1) == 1
elseif kmode == 7 then --// B
inst.K = k[inst.B + 1]
elseif kmode == 8 then --// AUX number low 16 bits
inst.K = bit32_band(inst.aux, 0xf)
end
end
local function readProto(bytecodeid)
local maxstacksize = readByte()
local numparams = readByte()
local nups = readByte()
local isvararg = readByte() ~= 0
if luauVersion >= 4 then
readByte() --// flags
local typesize = readVarInt();
cursor = cursor + typesize;
end
local sizecode = readVarInt()
local codelist = table_create(sizecode)
local skipnext = false
for i = 1, sizecode do
if skipnext then
skipnext = false
continue
end
skipnext = readInstruction(codelist)
end
local debugcodelist = table_create(sizecode)
for i = 1, sizecode do
debugcodelist[i] = codelist[i].opcode
end
local sizek = readVarInt()
local klist = table_create(sizek)
for i = 1, sizek do
local kt = readByte()
local k
if kt == 0 then --// Nil
k = nil
elseif kt == 1 then --// Bool
k = readByte() ~= 0
elseif kt == 2 then --// Number
k = readDouble()
elseif kt == 3 then --// String
k = stringList[readVarInt()]
elseif kt == 4 then --// Import
k = readWord()
elseif kt == 5 then --// Table
local dataLength = readVarInt()
k = table_create(dataLength)
for i = 1, dataLength do
k[i] = readVarInt()
end
elseif kt == 6 then --// Closure
k = readVarInt()
elseif kt == 7 then --// Vector
local x,y,z,w = readFloat(), readFloat(), readFloat(), readFloat()
if luau_settings.vectorSize == 4 then
k = luau_settings.vectorCtor(x,y,z,w)
else
k = luau_settings.vectorCtor(x,y,z)
end
end
klist[i] = k
end
-- // 2nd pass to replace constant references in the instruction
for i = 1, sizecode do
checkkmode(codelist[i], klist)
end
local sizep = readVarInt()
local protolist = table_create(sizep)
for i = 1, sizep do
protolist[i] = readVarInt() + 1
end
local linedefined = readVarInt()
local debugnameindex = readVarInt()
local debugname
if debugnameindex ~= 0 then
debugname = stringList[debugnameindex]
else
debugname = "(??)"
end
-- // lineinfo
local lineinfoenabled = readByte() ~= 0
local instructionlineinfo = nil
if lineinfoenabled then
local linegaplog2 = readByte()
local intervals = bit32_rshift((sizecode - 1), linegaplog2) + 1
local lineinfo = table_create(sizecode)
local abslineinfo = table_create(intervals)
local lastoffset = 0
for j = 1, sizecode do
lastoffset += readByte()
lineinfo[j] = lastoffset
end
local lastline = 0
for j = 1, intervals do
lastline += readWord()
abslineinfo[j] = lastline % (2 ^ 32)
end
instructionlineinfo = table_create(sizecode)
for i = 1, sizecode do
--// p->abslineinfo[pc >> p->linegaplog2] + p->lineinfo[pc];
table_insert(instructionlineinfo, abslineinfo[bit32_rshift(i - 1, linegaplog2) + 1] + lineinfo[i])
end
end
-- // debuginfo
if readByte() ~= 0 then
local sizel = readVarInt()
for i = 1, sizel do
readVarInt()
readVarInt()
readVarInt()
readByte()
end
local sizeupvalues = readVarInt()
for i = 1, sizeupvalues do
readVarInt()
end
end
return {
maxstacksize = maxstacksize;
numparams = numparams;
nups = nups;
isvararg = isvararg;
linedefined = linedefined;
debugname = debugname;
sizecode = sizecode;
code = codelist;
debugcode = debugcodelist;
sizek = sizek;
k = klist;
sizep = sizep;
protos = protolist;
lineinfoenabled = lineinfoenabled;
instructionlineinfo = instructionlineinfo;
bytecodeid = bytecodeid;
}
end
-- userdataRemapping (not used in VM, left unused)
if typesVersion == 3 then
local index = readByte()
while index ~= 0 do
readVarInt()
index = readByte()
end
end
local protoCount = readVarInt()
local protoList = table_create(protoCount)
for i = 1, protoCount do
protoList[i] = readProto(i - 1)
end
local mainProto = protoList[readVarInt() + 1]
assert(cursor == buffer_len(stream), "deserializer cursor position mismatch")
mainProto.debugname = "(main)"
return {
stringList = stringList;
protoList = protoList;
mainProto = mainProto;
typesVersion = typesVersion;
}
end
local function luau_load(module, env, luau_settings)
if luau_settings == nil then
luau_settings = luau_newsettings()
else
luau_validatesettings(luau_settings)
end
if type(module) ~= "table" then
module = luau_deserialize(module, luau_settings)
end
local protolist = module.protoList
local mainProto = module.mainProto
local breakHook = luau_settings.callHooks.breakHook
local stepHook = luau_settings.callHooks.stepHook
local interruptHook = luau_settings.callHooks.interruptHook
local panicHook = luau_settings.callHooks.panicHook
local alive = true
local function luau_close()
alive = false
end
local function luau_wrapclosure(module, proto, upvals)
local function luau_execute(...)
local debugging, stack, protos, code, varargs
if luau_settings.errorHandling then
debugging, stack, protos, code, varargs = ...
else
--// Copied from error handling wrapper
local passed = table_pack(...)
stack = table_create(proto.maxstacksize)
varargs = {
len = 0,
list = {},
}
table_move(passed, 1, proto.numparams, 0, stack)
if proto.numparams < passed.n then
local start = proto.numparams + 1
local len = passed.n - proto.numparams
varargs.len = len
table_move(passed, start, start + len - 1, 1, varargs.list)
end
passed = nil
debugging = {pc = 0, name = "NONE"}
protos = proto.protos
code = proto.code
end
local top, pc, open_upvalues, generalized_iterators = -1, 1, setmetatable({}, {__mode = "vs"}), setmetatable({}, {__mode = "ks"})
local constants = proto.k
local debugopcodes = proto.debugcode
local extensions = luau_settings.extensions
local handlingBreak = false
local inst, op
while alive do
if not handlingBreak then
inst = code[pc]
op = inst.opcode
end
handlingBreak = false
debugging.pc = pc
debugging.top = top
debugging.name = inst.opname
pc += 1
if stepHook then
stepHook(stack, debugging, proto, module, upvals)
end
if op == 0 then --[[ NOP ]]
--// Do nothing
elseif op == 1 then --[[ BREAK ]]
if breakHook then
local results = table.pack(breakHook(stack, debugging, proto, module, upvals))
if results[1] then
return table_unpack(results, 2, #results)
end
end
pc -= 1
op = debugopcodes[pc]
handlingBreak = true
elseif op == 2 then --[[ LOADNIL ]]
stack[inst.A] = nil
elseif op == 3 then --[[ LOADB ]]
stack[inst.A] = inst.B == 1
pc += inst.C
elseif op == 4 then --[[ LOADN ]]
stack[inst.A] = inst.D
elseif op == 5 then --[[ LOADK ]]
stack[inst.A] = inst.K
elseif op == 6 then --[[ MOVE ]]
stack[inst.A] = stack[inst.B]
elseif op == 7 then --[[ GETGLOBAL ]]
local kv = inst.K
stack[inst.A] = extensions[kv] or env[kv]
pc += 1 --// adjust for aux
elseif op == 8 then --[[ SETGLOBAL ]]
local kv = inst.K
env[kv] = stack[inst.A]
pc += 1 --// adjust for aux
elseif op == 9 then --[[ GETUPVAL ]]
local uv = upvals[inst.B + 1]
stack[inst.A] = uv.store[uv.index]
elseif op == 10 then --[[ SETUPVAL ]]
local uv = upvals[inst.B + 1]
uv.store[uv.index] = stack[inst.A]
elseif op == 11 then --[[ CLOSEUPVALS ]]
for i, uv in open_upvalues do
if uv.index >= inst.A then
uv.value = uv.store[uv.index]
uv.store = uv
uv.index = "value" --// self reference
open_upvalues[i] = nil
end
end
elseif op == 12 then --[[ GETIMPORT ]]
if luau_settings.useImportConstants then
stack[inst.A] = inst.K
else
local count = inst.KC
local k0 = inst.K0
local import = extensions[k0] or env[k0]
if count == 1 then
stack[inst.A] = import
elseif count == 2 then
stack[inst.A] = import[inst.K1]
elseif count == 3 then
stack[inst.A] = import[inst.K1][inst.K2]
end
end
pc += 1 --// adjust for aux
elseif op == 13 then --[[ GETTABLE ]]
stack[inst.A] = stack[inst.B][stack[inst.C]]
elseif op == 14 then --[[ SETTABLE ]]
stack[inst.B][stack[inst.C]] = stack[inst.A]
elseif op == 15 then --[[ GETTABLEKS ]]
local index = inst.K
stack[inst.A] = stack[inst.B][index]
pc += 1 --// adjust for aux
elseif op == 16 then --[[ SETTABLEKS ]]
local index = inst.K
stack[inst.B][index] = stack[inst.A]
pc += 1 --// adjust for aux
elseif op == 17 then --[[ GETTABLEN ]]
stack[inst.A] = stack[inst.B][inst.C + 1]
elseif op == 18 then --[[ SETTABLEN ]]
stack[inst.B][inst.C + 1] = stack[inst.A]
elseif op == 19 then --[[ NEWCLOSURE ]]
local newPrototype = protolist[protos[inst.D + 1]]
local nups = newPrototype.nups
local upvalues = table_create(nups)
stack[inst.A] = luau_wrapclosure(module, newPrototype, upvalues)
for i = 1, nups do
local pseudo = code[pc]
pc += 1
local type = pseudo.A
if type == 0 then --// value
local upvalue = {
value = stack[pseudo.B],
index = "value",--// self reference
}
upvalue.store = upvalue
upvalues[i] = upvalue
elseif type == 1 then --// reference
local index = pseudo.B
local prev = open_upvalues[index]
if prev == nil then
prev = {
index = index,
store = stack,
}
open_upvalues[index] = prev
end
upvalues[i] = prev
elseif type == 2 then --// upvalue
upvalues[i] = upvals[pseudo.B + 1]
end
end
elseif op == 20 then --[[ NAMECALL ]]
local A = inst.A
local B = inst.B
local kv = inst.K
local sb = stack[B]
stack[A + 1] = sb
pc += 1 --// adjust for aux
local useFallback = true
--// Special handling for native namecall behaviour
local useNativeHandler = luau_settings.useNativeNamecall
if useNativeHandler then
local nativeNamecall = luau_settings.namecallHandler
local callInst = code[pc]
local callOp = callInst.opcode
--// Copied from the CALL handler under
local callA, callB, callC = callInst.A, callInst.B, callInst.C
if stepHook then
stepHook(stack, debugging, proto, module, upvals)
end
if interruptHook then
interruptHook(stack, debugging, proto, module, upvals)
end
local params = if callB == 0 then top - callA else callB - 1
local ret_list = table_pack(
nativeNamecall(kv, table_unpack(stack, callA + 1, callA + params))
)
if ret_list[1] == true then
useFallback = false
pc += 1 --// Skip next CALL instruction
inst = callInst
op = callOp
debugging.pc = pc
debugging.name = inst.opname
table_remove(ret_list, 1)
local ret_num = ret_list.n - 1
if callC == 0 then
top = callA + ret_num - 1
else
ret_num = callC - 1
end
table_move(ret_list, 1, ret_num, callA, stack)
end
end
if useFallback then
stack[A] = sb[kv]
end
elseif op == 21 then --[[ CALL ]]
if interruptHook then
interruptHook(stack, debugging, proto, module, upvals)
end
local A, B, C = inst.A, inst.B, inst.C
local params = if B == 0 then top - A else B - 1
local func = stack[A]
local ret_list = table_pack(
func(table_unpack(stack, A + 1, A + params))
)
local ret_num = ret_list.n
if C == 0 then
top = A + ret_num - 1
else
ret_num = C - 1
end
table_move(ret_list, 1, ret_num, A, stack)
elseif op == 22 then --[[ RETURN ]]
if interruptHook then
interruptHook(stack, debugging, proto, module, upvals)
end
local A = inst.A
local B = inst.B
local b = B - 1
local nresults
if b == LUA_MULTRET then
nresults = top - A + 1
else
nresults = B - 1
end
return table_unpack(stack, A, A + nresults - 1)
elseif op == 23 then --[[ JUMP ]]
pc += inst.D
elseif op == 24 then --[[ JUMPBACK ]]
if interruptHook then
interruptHook(stack, debugging, proto, module, upvals)
end
pc += inst.D
elseif op == 25 then --[[ JUMPIF ]]
if stack[inst.A] then
pc += inst.D
end
elseif op == 26 then --[[ JUMPIFNOT ]]
if not stack[inst.A] then
pc += inst.D
end
elseif op == 27 then --[[ JUMPIFEQ ]]
if stack[inst.A] == stack[inst.aux] then
pc += inst.D
else
pc += 1
end
elseif op == 28 then --[[ JUMPIFLE ]]
if stack[inst.A] <= stack[inst.aux] then
pc += inst.D
else
pc += 1
end
elseif op == 29 then --[[ JUMPIFLT ]]
if stack[inst.A] < stack[inst.aux] then
pc += inst.D
else
pc += 1
end
elseif op == 30 then --[[ JUMPIFNOTEQ ]]
if stack[inst.A] == stack[inst.aux] then
pc += 1
else
pc += inst.D
end
elseif op == 31 then --[[ JUMPIFNOTLE ]]
if stack[inst.A] <= stack[inst.aux] then
pc += 1
else
pc += inst.D
end
elseif op == 32 then --[[ JUMPIFNOTLT ]]
if stack[inst.A] < stack[inst.aux] then
pc += 1
else
pc += inst.D
end
elseif op == 33 then --[[ ADD ]]
stack[inst.A] = stack[inst.B] + stack[inst.C]
elseif op == 34 then --[[ SUB ]]
stack[inst.A] = stack[inst.B] - stack[inst.C]
elseif op == 35 then --[[ MUL ]]
stack[inst.A] = stack[inst.B] * stack[inst.C]
elseif op == 36 then --[[ DIV ]]
stack[inst.A] = stack[inst.B] / stack[inst.C]
elseif op == 37 then --[[ MOD ]]
stack[inst.A] = stack[inst.B] % stack[inst.C]
elseif op == 38 then --[[ POW ]]
stack[inst.A] = stack[inst.B] ^ stack[inst.C]
elseif op == 39 then --[[ ADDK ]]
stack[inst.A] = stack[inst.B] + inst.K
elseif op == 40 then --[[ SUBK ]]
stack[inst.A] = stack[inst.B] - inst.K
elseif op == 41 then --[[ MULK ]]
stack[inst.A] = stack[inst.B] * inst.K
elseif op == 42 then --[[ DIVK ]]
stack[inst.A] = stack[inst.B] / inst.K
elseif op == 43 then --[[ MODK ]]
stack[inst.A] = stack[inst.B] % inst.K
elseif op == 44 then --[[ POWK ]]
stack[inst.A] = stack[inst.B] ^ inst.K
elseif op == 45 then --[[ AND ]]
local value = stack[inst.B]
stack[inst.A] = if value then stack[inst.C] or false else value
elseif op == 46 then --[[ OR ]]
local value = stack[inst.B]
stack[inst.A] = if value then value else stack[inst.C] or false
elseif op == 47 then --[[ ANDK ]]
local value = stack[inst.B]
stack[inst.A] = if value then inst.K or false else value
elseif op == 48 then --[[ ORK ]]
local value = stack[inst.B]
stack[inst.A] = if value then value else inst.K or false
elseif op == 49 then --[[ CONCAT ]]
local s = ""
for i = inst.B, inst.C do
s ..= stack[i]
end
stack[inst.A] = s
elseif op == 50 then --[[ NOT ]]
stack[inst.A] = not stack[inst.B]
elseif op == 51 then --[[ MINUS ]]
stack[inst.A] = -stack[inst.B]
elseif op == 52 then --[[ LENGTH ]]
stack[inst.A] = #stack[inst.B]
elseif op == 53 then --[[ NEWTABLE ]]
stack[inst.A] = table_create(inst.aux)
pc += 1 --// adjust for aux
elseif op == 54 then --[[ DUPTABLE ]]
local template = inst.K
local serialized = {}
for _, id in template do
serialized[constants[id + 1]] = nil
end
stack[inst.A] = serialized
elseif op == 55 then --[[ SETLIST ]]
local A = inst.A
local B = inst.B
local c = inst.C - 1
if c == LUA_MULTRET then
c = top - B + 1
end
table_move(stack, B, B + c - 1, inst.aux, stack[A])
pc += 1 --// adjust for aux
elseif op == 56 then --[[ FORNPREP ]]
local A = inst.A
local limit = stack[A]
if not ttisnumber(limit) then
local number = tonumber(limit)
if number == nil then
error("invalid 'for' limit (number expected)")
end
stack[A] = number
limit = number
end
local step = stack[A + 1]
if not ttisnumber(step) then
local number = tonumber(step)
if number == nil then
error("invalid 'for' step (number expected)")
end
stack[A + 1] = number
step = number
end
local index = stack[A + 2]
if not ttisnumber(index) then
local number = tonumber(index)
if number == nil then
error("invalid 'for' index (number expected)")
end
stack[A + 2] = number
index = number
end
if step > 0 then
if not (index <= limit) then
pc += inst.D
end
else
if not (limit <= index) then
pc += inst.D
end
end
elseif op == 57 then --[[ FORNLOOP ]]
if interruptHook then
interruptHook(stack, debugging, proto, module, upvals)
end
local A = inst.A
local limit = stack[A]
local step = stack[A + 1]
local index = stack[A + 2] + step
stack[A + 2] = index
if step > 0 then
if index <= limit then
pc += inst.D
end
else
if limit <= index then
pc += inst.D
end
end
elseif op == 58 then --[[ FORGLOOP ]]
if interruptHook then
interruptHook(stack, debugging, proto, module, upvals)
end
local A = inst.A
local res = inst.K
top = A + 6
local it = stack[A]
if (luau_settings.generalizedIteration == false) or ttisfunction(it) then
local vals = { it(stack[A + 1], stack[A + 2]) }
table_move(vals, 1, res, A + 3, stack)
if stack[A + 3] ~= nil then
stack[A + 2] = stack[A + 3]
pc += inst.D
else
pc += 1
end
else
local ok, vals = coroutine_resume(generalized_iterators[inst], it, stack[A + 1], stack[A + 2])
if not ok then
error(vals)
end
if vals == LUA_GENERALIZED_TERMINATOR then
generalized_iterators[inst] = nil
pc += 1
else
table_move(vals, 1, res, A + 3, stack)
stack[A + 2] = stack[A + 3]
pc += inst.D
end
end
elseif op == 59 then --[[ FORGPREP_INEXT ]]
if not ttisfunction(stack[inst.A]) then
error(string_format("attempt to iterate over a %s value", type(stack[inst.A]))) -- FORGPREP_INEXT encountered non-function value
end
pc += inst.D
elseif op == 60 then --[[ FASTCALL3 ]]
--[[ Skipped ]]
pc += 1 --// adjust for aux
elseif op == 61 then --[[ FORGPREP_NEXT ]]
if not ttisfunction(stack[inst.A]) then
error(string_format("attempt to iterate over a %s value", type(stack[inst.A]))) -- FORGPREP_NEXT encountered non-function value
end
pc += inst.D
elseif op == 63 then --[[ GETVARARGS ]]
local A = inst.A
local b = inst.B - 1
if b == LUA_MULTRET then
b = varargs.len
top = A + b - 1
end
table_move(varargs.list, 1, b, A, stack)
elseif op == 64 then --[[ DUPCLOSURE ]]
local newPrototype = protolist[inst.K + 1] --// correct behavior would be to reuse the prototype if possible but it would not be useful here
local nups = newPrototype.nups
local upvalues = table_create(nups)
stack[inst.A] = luau_wrapclosure(module, newPrototype, upvalues)
for i = 1, nups do
local pseudo = code[pc]
pc += 1
local type = pseudo.A
if type == 0 then --// value
local upvalue = {
value = stack[pseudo.B],
index = "value",--// self reference
}
upvalue.store = upvalue
upvalues[i] = upvalue
--// references dont get handled by DUPCLOSURE
elseif type == 2 then --// upvalue
upvalues[i] = upvals[pseudo.B + 1]
end
end
elseif op == 65 then --[[ PREPVARARGS ]]
--[[ Handled by wrapper ]]
elseif op == 66 then --[[ LOADKX ]]
local kv = inst.K
stack[inst.A] = kv
pc += 1 --// adjust for aux
elseif op == 67 then --[[ JUMPX ]]
if interruptHook then
interruptHook(stack, debugging, proto, module, upvals)
end
pc += inst.E
elseif op == 68 then --[[ FASTCALL ]]
--[[ Skipped ]]
elseif op == 69 then --[[ COVERAGE ]]
inst.E += 1
elseif op == 70 then --[[ CAPTURE ]]
--[[ Handled by CLOSURE ]]
error("encountered unhandled CAPTURE")
elseif op == 71 then --[[ SUBRK ]]
stack[inst.A] = inst.K - stack[inst.C]
elseif op == 72 then --[[ DIVRK ]]
stack[inst.A] = inst.K / stack[inst.C]
elseif op == 73 then --[[ FASTCALL1 ]]
--[[ Skipped ]]
elseif op == 74 then --[[ FASTCALL2 ]]
--[[ Skipped ]]
pc += 1 --// adjust for aux
elseif op == 75 then --[[ FASTCALL2K ]]
--[[ Skipped ]]
pc += 1 --// adjust for aux
elseif op == 76 then --[[ FORGPREP ]]
local iterator = stack[inst.A]
if luau_settings.generalizedIteration and not ttisfunction(iterator) then
local loopInstruction = code[pc + inst.D]
if generalized_iterators[loopInstruction] == nil then
local function gen_iterator(...)
for r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, r30, r31, r32, r33, r34, r35, r36, r37, r38, r39, r40, r41, r42, r43, r44, r45, r46, r47, r48, r49, r50, r51, r52, r53, r54, r55, r56, r57, r58, r59, r60, r61, r62, r63, r64, r65, r66, r67, r68, r69, r70, r71, r72, r73, r74, r75, r76, r77, r78, r79, r80, r81, r82, r83, r84, r85, r86, r87, r88, r89, r90, r91, r92, r93, r94, r95, r96, r97, r98, r99, r100, r101, r102, r103, r104, r105, r106, r107, r108, r109, r110, r111, r112, r113, r114, r115, r116, r117, r118, r119, r120, r121, r122, r123, r124, r125, r126, r127, r128, r129, r130, r131, r132, r133, r134, r135, r136, r137, r138, r139, r140, r141, r142, r143, r144, r145, r146, r147, r148, r149, r150, r151, r152, r153, r154, r155, r156, r157, r158, r159, r160, r161, r162, r163, r164, r165, r166, r167, r168, r169, r170, r171, r172, r173, r174, r175, r176, r177, r178, r179, r180, r181, r182, r183, r184, r185, r186, r187, r188, r189, r190, r191, r192, r193, r194, r195, r196, r197, r198, r199, r200 in ... do
coroutine_yield({r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, r30, r31, r32, r33, r34, r35, r36, r37, r38, r39, r40, r41, r42, r43, r44, r45, r46, r47, r48, r49, r50, r51, r52, r53, r54, r55, r56, r57, r58, r59, r60, r61, r62, r63, r64, r65, r66, r67, r68, r69, r70, r71, r72, r73, r74, r75, r76, r77, r78, r79, r80, r81, r82, r83, r84, r85, r86, r87, r88, r89, r90, r91, r92, r93, r94, r95, r96, r97, r98, r99, r100, r101, r102, r103, r104, r105, r106, r107, r108, r109, r110, r111, r112, r113, r114, r115, r116, r117, r118, r119, r120, r121, r122, r123, r124, r125, r126, r127, r128, r129, r130, r131, r132, r133, r134, r135, r136, r137, r138, r139, r140, r141, r142, r143, r144, r145, r146, r147, r148, r149, r150, r151, r152, r153, r154, r155, r156, r157, r158, r159, r160, r161, r162, r163, r164, r165, r166, r167, r168, r169, r170, r171, r172, r173, r174, r175, r176, r177, r178, r179, r180, r181, r182, r183, r184, r185, r186, r187, r188, r189, r190, r191, r192, r193, r194, r195, r196, r197, r198, r199, r200})
end
coroutine_yield(LUA_GENERALIZED_TERMINATOR)
end
generalized_iterators[loopInstruction] = coroutine_create(gen_iterator)
end
end
pc += inst.D
elseif op == 77 then --[[ JUMPXEQKNIL ]]
local kn = inst.KN
if (stack[inst.A] == nil) ~= kn then
pc += inst.D
else
pc += 1
end
elseif op == 78 then --[[ JUMPXEQKB ]]
local kv = inst.K
local kn = inst.KN
local ra = stack[inst.A]
if (ttisboolean(ra) and (ra == kv)) ~= kn then
pc += inst.D
else
pc += 1
end
elseif op == 79 then --[[ JUMPXEQKN ]]
local kv = inst.K
local kn = inst.KN
local ra = stack[inst.A]
if (ra == kv) ~= kn then
pc += inst.D
else
pc += 1
end
elseif op == 80 then --[[ JUMPXEQKS ]]
local kv = inst.K
local kn = inst.KN
local ra = stack[inst.A]
if (ra == kv) ~= kn then
pc += inst.D
else
pc += 1
end
elseif op == 81 then --[[ IDIV ]]
stack[inst.A] = stack[inst.B] // stack[inst.C]
elseif op == 82 then --[[ IDIVK ]]
stack[inst.A] = stack[inst.B] // inst.K
else
error("Unsupported Opcode: " .. inst.opname .. " op: " .. op)
end
end
for i, uv in open_upvalues do
uv.value = uv.store[uv.index]
uv.store = uv
uv.index = "value" --// self reference
open_upvalues[i] = nil
end
for i, iter in generalized_iterators do
coroutine_close(iter)
generalized_iterators[i] = nil
end
end
local function wrapped(...)
local passed = table_pack(...)
local stack = table_create(proto.maxstacksize)
local varargs = {
len = 0,
list = {},
}
table_move(passed, 1, proto.numparams, 0, stack)
if proto.numparams < passed.n then
local start = proto.numparams + 1
local len = passed.n - proto.numparams
varargs.len = len
table_move(passed, start, start + len - 1, 1, varargs.list)
end
passed = nil
local debugging = {pc = 0, name = "NONE"}
local result
if luau_settings.errorHandling then
result = table_pack(pcall(luau_execute, debugging, stack, proto.protos, proto.code, varargs))
else
result = table_pack(true, luau_execute(debugging, stack, proto.protos, proto.code, varargs))
end
if result[1] then
return table_unpack(result, 2, result.n)
else
local message = result[2]
if panicHook then
panicHook(message, stack, debugging, proto, module, upvals)
end
if ttisstring(message) == false then
if luau_settings.allowProxyErrors then
error(message)
else
message = type(message)
end
end
if proto.lineinfoenabled then
return error(string_format("Fiu VM Error { Name: %s Line: %s PC: %s Opcode: %s }: %s", proto.debugname, proto.instructionlineinfo[debugging.pc], debugging.pc, debugging.name, message), 0)
else
return error(string_format("Fiu VM Error { Name: %s PC: %s Opcode: %s }: %s", proto.debugname, debugging.pc, debugging.name, message), 0)
end
end
end
if luau_settings.errorHandling then
return wrapped
else
return luau_execute
end
end
return luau_wrapclosure(module, mainProto), luau_close
end
return {
luau_newsettings = luau_newsettings,
luau_validatesettings = luau_validatesettings,
luau_deserialize = luau_deserialize,
luau_load = luau_load,
}
end)()
local base64 = (function()
-- @original: https://gist.github.com/Reselim/40d62b17d138cc74335a1b0709e19ce2
local Alphabet = {}
local Indexes = {}
-- A-Z
for Index = 65, 90 do
table.insert(Alphabet, Index)
end
-- a-z
for Index = 97, 122 do
table.insert(Alphabet, Index)
end
-- 0-9
for Index = 48, 57 do
table.insert(Alphabet, Index)
end
table.insert(Alphabet, 43) -- +
table.insert(Alphabet, 47) -- /
for Index, Character in ipairs(Alphabet) do
Indexes[Character] = Index
end
local Base64 = {}
local bit32_rshift = bit32.rshift
local bit32_lshift = bit32.lshift
local bit32_band = bit32.band
--[[**
Encodes a string in Base64.
@param [t:string] Input The input string to encode.
@returns [t:string] The string encoded in Base64.
**--]]
function Base64.encode(Input)
local Output = {}
local Length = 0
for Index = 1, #Input, 3 do
local C1, C2, C3 = string.byte(Input, Index, Index + 2)
local A = bit32_rshift(C1, 2)
local B = bit32_lshift(bit32_band(C1, 3), 4) + bit32_rshift(C2 or 0, 4)
local C = bit32_lshift(bit32_band(C2 or 0, 15), 2) + bit32_rshift(C3 or 0, 6)
local D = bit32_band(C3 or 0, 63)
Length = Length + 1
Output[Length] = Alphabet[A + 1]
Length = Length + 1
Output[Length] = Alphabet[B + 1]
Length = Length + 1
Output[Length] = C2 and Alphabet[C + 1] or 61
Length = Length + 1
Output[Length] = C3 and Alphabet[D + 1] or 61
end
local NewOutput = {}
local NewLength = 0
local IndexAdd4096Sub1
for Index = 1, Length, 4096 do
NewLength = NewLength + 1
IndexAdd4096Sub1 = Index + 4096 - 1
NewOutput[NewLength] = string.char(table.unpack(
Output,
Index,
IndexAdd4096Sub1 > Length and Length or IndexAdd4096Sub1
))
end
return table.concat(NewOutput)
end
--[[**
Decodes a string from Base64.
@param [t:string] Input The input string to decode.
@returns [t:string] The newly decoded string.
**--]]
function Base64.decode(Input)
local Output = {}
local Length = 0
for Index = 1, #Input, 4 do
local C1, C2, C3, C4 = string.byte(Input, Index, Index + 3)
local I1 = Indexes[C1] - 1
local I2 = Indexes[C2] - 1
local I3 = (Indexes[C3] or 1) - 1
local I4 = (Indexes[C4] or 1) - 1
local A = bit32_lshift(I1, 2) + bit32_rshift(I2, 4)
local B = bit32_lshift(bit32_band(I2, 15), 4) + bit32_rshift(I3, 2)
local C = bit32_lshift(bit32_band(I3, 3), 6) + I4
Length = Length + 1
Output[Length] = A
if C3 ~= 61 then
Length = Length + 1
Output[Length] = B
end
if C4 ~= 61 then
Length = Length + 1
Output[Length] = C
end
end
local NewOutput = {}
local NewLength = 0
local IndexAdd4096Sub1
for Index = 1, Length, 4096 do
NewLength = NewLength + 1
IndexAdd4096Sub1 = Index + 4096 - 1
NewOutput[NewLength] = string.char(table.unpack(
Output,
Index,
IndexAdd4096Sub1 > Length and Length or IndexAdd4096Sub1
))
end
return table.concat(NewOutput)
end
return Base64
end)()
local env = {}
local filesystem = {} do
filesystem.__index = filesystem -- setmetatable(filesystem, cfilesystem) for filesystem wrappers
local currentTime = tick
local DEBUG = false
filesystem.umask = 022
filesystem.types = {
dir = "di",
link = "ln",
socket = "so",
pipe = "pi",
exe = "ex",
file = "fl"
}
function filesystem.extname(path)
local dot_index = path:match(".*()%.")
if not dot_index or dot_index == 1 then
return ""
end
return path:sub(dot_index)
end
function filesystem.join(...)
local parts = {...}
local result = {}
for _, path in ipairs(parts) do
path = path:gsub("^/+", "")
path = path:gsub("/+$", "")
if path and path ~= "" then
table.insert(result, path)
end
end
return "/" .. table.concat(result, "/") .. "/"
end
function filesystem.basename(path)
path = path:gsub("^/+", "")
path = path:gsub("/+$", "")
local base = path:match("([^/]+)$")
return base or ""
end
local function createNode(type, content)
return {
type = type,
metadata = {
created = currentTime(),
modified = currentTime(),
permissions = type == "file" and 666 - filesystem.umask or 777 - filesystem.umask,
},
content = type == filesystem.types.file and content or nil,
contents = type == filesystem.types.dir and (content or {}) or nil
}
end
function filesystem.splitpath(path)
local dirs = {}
local stack = {}
for dir in path:gmatch("[^/]+") do
if dir == ".." then
if #stack > 0 then
table.remove(stack)
end
elseif dir ~= "." and dir ~= "" then
table.insert(stack, dir)
end
end
for i, dir in ipairs(stack) do
table.insert(dirs, dir)
end
return dirs
end
function filesystem.prefix(path, prefix, dir)
if path:sub(1, #prefix) == prefix then
path = filesystem.join(dir, path:sub(#prefix + 1))
end
if path:sub(1, #prefix + 1) == "/" .. prefix or path:sub(1, #prefix + 1) == prefix .. "/" then
path = filesystem.join(dir, path:sub(#prefix + 2))
end
return path
end
function filesystem.parsecontext(path, home, dir)
path = filesystem.prefix(path, "~")
path = filesystem.prefix(path, ".")
path = table.concat(filesystem.splitpath(path), "/")
if #path == 0 then
path = "/"
end
return path
end
local function getNode(currentNode, path)
local dirs = filesystem.splitpath(path)
for _, dir in ipairs(dirs) do
currentNode = currentNode.contents and currentNode.contents[dir]
if not currentNode then
return nil
end
end
return currentNode
end
local function getDir(fs, path)
local dirs = filesystem.splitpath(path)
local parentPath = table.concat(dirs, "/", 1, #dirs - 1)
local parentDir = getNode(fs, parentPath)
if not parentDir or parentDir.type ~= filesystem.types.dir then
return false, "No such directory: " .. parentPath
end
return true, parentDir, dirs
end
function filesystem.mkdir(fs, path)
if DEBUG then print("mkdir", debug.traceback()) end
local success, result, dirs = getDir(fs, path)
if not success then
return success, result
end
local name = dirs[#dirs]
if result.contents[name] then
return false, "Directory already exists: " .. path
end
result.contents[name] = createNode(filesystem.types.dir)
return true
end
function filesystem.rmdir(fs, path)
if DEBUG then print("rmdir", debug.traceback()) end
local success, result, dirs = getDir(fs, path)
if not success then
return success, result
end
local name = dirs[#dirs]
if not result.contents[name] then
return false, "No such directory: " .. path
end
result.contents[name] = nil
return true
end
function filesystem.create(fs, path, content, metadata)
if DEBUG then print("create", debug.traceback()) end
local success, result, dirs = getDir(fs, path)
if not success then
return success, result
end
local name = dirs[#dirs]
if result.contents[name] then
return false, "File already exists: " .. path
end
local node = createNode(filesystem.types.file, content)
if metadata then
for index, value in metadata do
node.metadata[index] = value
end
end
result.contents[name] = node
return true
end
function filesystem.rm(fs, path)
if DEBUG then print("rm", debug.traceback()) end
local success, result, dirs = getDir(fs, path)
if not success then
return success, result
end
local name = dirs[#dirs]
if not result.contents[name] then
return false, "No such file: " .. path
end
result.contents[name] = nil
return true
end
function filesystem.readdir(fs, path)
if DEBUG then print("readdir", debug.traceback()) end
local success, result, dirs = getDir(fs, path)
if not success then
return success, result
end
local contents = {}
if path == "/" then
contents = result.contents
else
local name = dirs[#dirs]
if not result.contents[name] or result.contents[name].type ~= filesystem.types.dir then
return false, "No such directory: " .. path
end
contents = result.contents[name].contents
end
local paths = {}
for node in contents do
table.insert(paths, filesystem.join(path, node))
end
return true, paths
end
function filesystem.read(fs, path)
if DEBUG then print("read", debug.traceback()) end
local file = getNode(fs, path)
if file and file.type == filesystem.types.file then
return true, file.content
else
return false, "No such file: " .. path
end
end
function filesystem.write(fs, path, content, metadata)
if DEBUG then print("write", debug.traceback()) end
local file = getNode(fs, path)
if file and file.type == filesystem.types.file then
file.content = content
file.metadata.modified = currentTime()
if metadata then
for index, value in metadata do
file.metadata[index] = value
end
end
return true
else
return filesystem.create(fs, path, content, metadata)
end
end
function filesystem.metadata(fs, path)
if DEBUG then print("metadata", debug.traceback()) end
local node = getNode(fs, path)
if node then
local metadata = node.metadata
metadata.type = node.type
metadata.subtype = node.subtype
return true, metadata
end
return false, "No such file or directory: " .. path
end
function filesystem.symlink(fs, path1, path2)
local node1 = getNode(fs, path1)
if not node1 then
return false, "No such file or directory: " .. path1
end
local node2 = getNode(fs, path2)
if not node2 then
return false, "No such file or directory: " .. path2
end
node1.content = nil
node1.contents = nil
node1.subtype = filesystem.types.link
setmetatable(node1, {
__index = function(_, index)
if index == "metadata" or index == "subtype" then
return node1[index]
end
return node2[index]
end,
__newindex = function(_, index, value)
node2[index] = value
end,
})
return true
end
function filesystem.exists(fs, path)
local node = getNode(fs, path)
return node ~= nil and true or false
end
function filesystem.new()
if DEBUG then print("new", debug.traceback()) end
return createNode(filesystem.types.dir)
end
end
local fs = filesystem.new() do
local rootfs = {
"/bin",
"/boot",
"/dev",
"/etc",
"/etc/default",
"/etc/network",
"/etc/profile",
"/etc/rc.d",
"/etc/xdg",
"/home",
"/lib",
"/lib64",
"/media",
"/mnt",
"/opt",
"/proc",
"/root",
"/run",
"/sbin",
"/srv",
"/sys",
"/tmp",
"/usr",
"/usr/bin",
"/usr/lib",
"/usr/lib64",
"/usr/local",
"/usr/local/bin",
"/usr/local/etc",
"/usr/local/games",
"/usr/local/lib",
"/usr/local/sbin",
"/usr/local/share",
"/usr/local/src",
"/usr/sbin",
"/usr/share",
"/usr/share/doc",
"/usr/share/man",
"/usr/src",
"/var",
"/var/cache",
"/var/lib",
"/var/lib/machines",
"/var/lib/pacman",
"/var/lib/systemd",
"/var/lib/dbus",
"/var/lib/logrotate",
"/var/local",
"/var/lock",
"/var/log",
"/var/mail",
"/var/opt",
"/var/run",
"/var/spool",
"/var/tmp"
}
local failed = false
local function safe(func, ...)
local success, result = func(...)
if not success then
failed = true
warn(result)
end
return success, result
end
for _, path in rootfs do
safe(filesystem.mkdir, fs, path)
end
safe(filesystem.symlink, fs, "/bin", "/usr/bin")
safe(filesystem.symlink, fs, "/lib", "/usr/lib")
safe(filesystem.symlink, fs, "/lib64", "/usr/lib64")
safe(filesystem.symlink, fs, "/sbin", "/usr/sbin")
safe(filesystem.write, fs, "/etc/machine-id", HttpService:GenerateGUID(false))
safe(filesystem.write, fs, "/etc/os-release", `NAME="Arch Linux"\nPRETTY_NAME="Arch Linux"\nID=arch\nBUILD_ID=rolling\nANSI_COLOR="38;2;23;147;209"\nHOME_URL="https://archlinux.org/"\nDOCUMENTATION_URL="https://wiki.archlinux.org/"\nSUPPORT_URL="https://bbs.archlinux.org/"\nBUG_REPORT_URL="https://gitlab.archlinux.org/groups/archlinux/-/issues"\nPRIVACY_POLICY_URL="https://terms.archlinux.org/docs/privacy-policy/"\nLOGO=archlinux-logo`)
safe(filesystem.write, fs, "/etc/issue", "Arch Linux \r (\l)")
safe(filesystem.write, fs, "/usr/bin/ls", base64.decode("BQENBGFyZ3MEc2VsZgNkaXIKZmlsZXN5c3RlbQhtZXRhZGF0YQR0eXBlBXR5cGVzB3JlYWRkaXIIYmFzZW5hbWUFdGFibGUGaW5zZXJ0BmNvbmNhdAEgAQ8AAAEAADpBAAAADAEBAAAAAEARAAEAGQACAAwABAAADCCADAEHAAAYUIAGAgAAFQECAxkBAwADAwAABgQCABYDAwAPAwJxCAAAAAwECgADJFDAHgMfAAQAAAAMAwwAACxQgAYEAAAVAwIDGgMfADUFAAAAAAAABgYEAAIHAAACCAAATAYJAAYMBQAMDQ4AADRQgAYOCgAVDQIARDQAAgwLEQAAQPCAFQsAAToG9v8CAAAAAwYBAAwHEwAASPCABggFAAUJFAAVBwMAFgYAABYAAQADAwEADAQOAAA0UIAGBQAAFQQCABYDAAAWAAEAFQMBBAAAAEADAgMDBAAMIIADBAMFBAAYUIADBgMHBAMkUMADCAQALFCAAwkEADRQgAMKAwsEAEDwgAMMBABI8IADDQABAAEYAAAAAAAAAAEAAAABAQAAAgAAAAAAAQAAAAEBAAEAAAABAAAAAAAAAAD/AAMAAAAAAAAE/wAAAAAAAQEAAAAAAA=="))
safe(filesystem.write, fs, "/usr/bin/cat", base64.decode("BQEIBGFyZ3MKZmlsZXN5c3RlbQhtZXRhZGF0YQR0eXBlBXR5cGVzA2RpchA6IElzIGEgZGlyZWN0b3J5BHJlYWQBBwAAAQIAHEEAAAAMAQEAAAAAQBEAAQAMAQQAAAwggAYCAAAVAQIDGQEBABYCAgAPAwJxBQAAAAwECAAHGCDAHgMGAAQAAAADAwAABgUAAAUGCQAxBAUGFgMDAAwDCwAAKCCABgQAABUDAgMDBQEABgYEABYFAwAMAwEEAAAAQAMCAwMEAAwggAMEAwUDBgQHGCDAAwcDCAQAKCCAAAEAARgAAAAAAQAAAAEBAgAAAAAAAQAAAAACAAAAAQAAAQAAAAAA"))
safe(filesystem.write, fs, "/usr/bin/clear", base64.decode("BQEDBHNlbGYFY2xlYXIpY2xlYXI6IHRlcm1pbmFsIGRvZXMgbm90IHN1cHBvcnQgY2xlYXJpbmcBAgAAAQIADkEAAAAMAAIAAAQAgBoABwAMAAMAAAAAQBQAALABAAAAFQACAQMAAQAWAAIAAwAAAAUBBAAWAAMABQMBAwIEAAQAgAQAAABAAwMAAQABGAAAAAABAAAAAAEAAgAAAQAAAAAA"))
safe(filesystem.write, fs, "/usr/bin/cd", base64.decode("BQEHAX4EYXJncwpmaWxlc3lzdGVtBmV4aXN0cxs6IE5vIHN1Y2ggZmlsZSBvciBkaXJlY3RvcnkCY2QeZmFpbGVkOiBhcmUgeW91IGluIGEgdGVybWluYWw/AQYAAAECABxBAAAADAICAAAAEEARAQIAMAABAAwBBQAAEDCABgIAABUBAgIZAQUAAwIAAAYEAAAFBQYAMQMEBRYCAwAMAggAAABwQBoCBQAMAggAAABwQAYDAAAVAgIBFwADAAMCAAAFAwkAFgIDAAMCAQAWAgIACgMBAwIEAAAQQAMDAwQEABAwgAMFAwYEAABwQAMHAAEAARgAAAAAAAEAAAABAQAAAAACAAABAAAAAAIAAAIAAQAAAAAA"))
safe(filesystem.write, fs, "/usr/bin/touch", base64.decode("BQEKBGFyZ3MHb3B0aW9ucwF0CG1vZGlmaWVkCmZpbGVzeXN0ZW0GZXhpc3RzBHJlYWQFd3JpdGUHY3JlYXRlZAABCQAAAQIALUEAAAAMAQEAAAAAQBEAAQAMAQQAAAwggAICAAAaAQQANgMGABABA6QFAAAABgIDAAwDCQAAIHCABgQAABUDAgIaAw0ADAMLAAAocIAGBAAAFQMCAxoDFgAMBQ0AADBwgAYGAAAGBwQABggCABUFBAAWBQAAFgABAA8DAqQFAAAAEAMC0A4AAAACAwAAEAMCpAUAAAAMAw0AADBwgAYEAAAFBQ8ABgYCABUDBAAWAwAAFgABABADAQQAAABAAwIDAwQADCCAAwQFAQUDBQMGBAAgcIADBwQAKHCAAwgEADBwgAMJAwoAAQABGAAAAAABAAEBAQEAAAMAAAAAAQAAAAEBAAAAAAAAB/wAAAABAAABAAAAAAAAAgEAAAAAAA=="))
safe(filesystem.write, fs, "/usr/bin/fastfetch-icon", base64.decode("BQETBlteDQpdKwZnbWF0Y2gMXiVzKiguLSklcyokBW1hdGNoAAJeIw5eKFsld19dKyk9KC4qKQheIiguKikiJAheJyguKiknJBNwYXJzZUtleVZhbHVlU3RyaW5nCmZpbGVzeXN0ZW0EcmVhZA8vZXRjL29zLXJlbGVhc2UCaWQFcGNhbGwEaHR0cAhHZXRBc3luY0xodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZmFzdGZldGNoLWNsaS9mYXN0ZmV0Y2gvZGV2L3NyYy9sb2dvL2FzY2lpBC50eHQCDAEAAAAAKTUBAAAAAAAABQQAABQCAIgBAAAAFQIDBEwCHwAFCQIAFAcF5wMAAAAVBwMCBgUHAFAFGQAEAAAABQkFABQHBecDAAAAFQcDAhkHEwAFCQYAFAcF5wMAAAAVBwMDGgcOABoIDQAFCwcAFAkI5wMAAAAVCQMCGQkGAAULCAAUCQjnAwAAABUJAwIZCQEABgkIAAYICQAOCAEHOgLg/wEAAAAWAQIACQMBAwIDAwMEAwUDBgMHAwgDCQABCgEYAAACAAAAAAEAAAAAAQAAAAAAAAEAAAACAAEAAAAAAAAAAAAAAAH5AAwCAAAAAAsAAAECACJBAAAAQAAAAAwBAwAACBCABQIEABUBAgMaARgABgMAAAYEAgAVAwICBgIDAA8DAvQFAAAADAQHAAAAYEAMBQoAACSAgAwGCwAAAIBABQgMAAYJAwAFCg0AMQcIChUEBAMaBAMAAwYBAAYHBQAWBgMAAwYAAAYHBQAWBgMAAwMAAAYEAgAWAwMADgYAAwsDDAQACBCAAw0DDgMPBAAAYEADEAMRBAAkgIAEAACAQAMSAxMBAAEAARgAABIAAAABAQAAAAEAAQAAAAAAAAAAAAABAQAAAgAAAwAAAQAAAAAB"))
safe(filesystem.write, fs, "/usr/bin/fastfetch-icon.luau", [[local function parseKeyValueString(str)
local result = {}
for line in str:gmatch("[^\r\n]+") do
line = line:match("^%s*(.-)%s*$")
if line ~= "" and not line:match("^#") then
local key, value = line:match("^([%w_]+)=(.*)")
if key and value then
value = value:match('^"(.*)"$') or value:match("^'(.*)'$") or value
result[key] = value
end
end
end
return result
end
local gotosrelease, osrelease = filesystem.read("/etc/os-release")
if gotosrelease then
osrelease = parseKeyValueString(osrelease)
local id = osrelease.ID
if not id then
return false, "invalid os-release parameters, missing ID"
end
local goticon, iconresponse = pcall(http.GetAsync, http, "https://raw.githubusercontent.com/fastfetch-cli/fastfetch/dev/src/logo/ascii/"..id..".txt")
if goticon then
return true, iconresponse
else
return false, iconresponse
end
else
return false, osrelease
end]])
if failed then
error("failed creating rootfs")
end
end
local term = Roact.Component:extend("term") do
local function parseCommand(commandString)
local command, argsString = commandString:match("^(%S+)%s*(.*)")
local args = {}
local pos = 1
local inQuotes = false
local currentArg = ""
while pos <= #argsString do
local char = argsString:sub(pos, pos)
if char == '"' then
inQuotes = not inQuotes
elseif char == ' ' and not inQuotes then
if #currentArg > 0 then
table.insert(args, currentArg)
currentArg = ""
end
else
currentArg = currentArg .. char
end
pos = pos + 1
end
if #currentArg > 0 then
table.insert(args, currentArg)
end
return command, args
end
local function parseOptions(args)
local parsedArgs = { options = {}, positional = {} }
local function handleArg(arg)
if arg:match("^%-%-") then
local key, value = arg:match("^%-%-(%S+)%s*(.*)")
parsedArgs.options[key] = value == "" and true or value
elseif arg:match("^%-%S") then
local key = arg:sub(2)
parsedArgs.options[key] = true
else
table.insert(parsedArgs.positional, arg)
end
end
for i = 1, #args do
handleArg(args[i])
end
return parsedArgs
end
term.colors = {
--basic
[0] = "#000000", -- Black
[1] = "#ff0000", -- Red
[2] = "#00ff00", -- Green
[3] = "#ffff00", -- Yellow
[4] = "#0000ff", -- Blue
[5] = "#ff00ff", -- Magenta
[6] = "#00ffff", -- Cyan
[7] = "#ffffff", -- White
[8] = "#808080", -- Bright Black (Gray)
[9] = "#ff6666", -- Bright Red
[10] = "#66ff66", -- Bright Green
[11] = "#ffff66", -- Bright Yellow
[12] = "#6666ff", -- Bright Blue
[13] = "#ff66ff", -- Bright Magenta
[14] = "#66ffff", -- Bright Cyan
[15] = "#ffffff", -- Bright White
--extended
[16] = "#000000", [17] = "#800000", [18] = "#008000", [19] = "#808000",
[20] = "#000080", [21] = "#800080", [22] = "#008080", [23] = "#c0c0c0",
[24] = "#808080", [25] = "#ff0000", [26] = "#00ff00", [27] = "#ffff00",
[28] = "#0000ff", [29] = "#ff00ff", [30] = "#00ffff", [31] = "#ffffff",
[32] = "#000000", [33] = "#00005f", [34] = "#000087", [35] = "#0000af",
[36] = "#0000d7", [37] = "#0000ff", [38] = "#005f00", [39] = "#005f5f",
[40] = "#005f87", [41] = "#005faf", [42] = "#007f00", [43] = "#007f5f",
[44] = "#007f87", [45] = "#007faf", [46] = "#007fd7", [47] = "#007fff",
[48] = "#00af00", [49] = "#00af5f", [50] = "#00af87", [51] = "#00afaf",
[52] = "#00afd7", [53] = "#00afff", [54] = "#00d700", [55] = "#00d75f",
[56] = "#00d787", [57] = "#00d7af", [58] = "#00d7d7", [59] = "#00d7ff",
[60] = "#00ff00", [61] = "#00ff5f", [62] = "#00ff87", [63] = "#00ffaf",
[64] = "#00ffd7", [65] = "#00ffff", [66] = "#5f0000", [67] = "#5f005f",
[68] = "#5f0087", [69] = "#5f00af", [70] = "#5f00d7", [71] = "#5f00ff",
[72] = "#5f5f00", [73] = "#5f5f5f", [74] = "#5f5f87", [75] = "#5f5faf",
[76] = "#5f5fd7", [77] = "#5f5fff", [78] = "#5f8700", [79] = "#5f875f",
[80] = "#5f8787", [81] = "#5f87af", [82] = "#5f87d7", [83] = "#5f87ff",
[84] = "#5faf00", [85] = "#5faf5f", [86] = "#5faf87", [87] = "#5fafaf",
[88] = "#5fafd7", [89] = "#5fafff", [90] = "#5fd700", [91] = "#5fd75f",
[92] = "#5fd787", [93] = "#5fd7af", [94] = "#5fd7d7", [95] = "#5fd7ff",
[96] = "#5fff00", [97] = "#5fff5f", [98] = "#5fff87", [99] = "#5fffaf",
[100] = "#5fffd7", [101] = "#5fffff", [102] = "#870000", [103] = "#87005f",
[104] = "#870087", [105] = "#8700af", [106] = "#8700d7", [107] = "#8700ff",
[108] = "#875f00", [109] = "#875f5f", [110] = "#875f87", [111] = "#875faf",
[112] = "#875fd7", [113] = "#875fff", [114] = "#878700", [115] = "#87875f",
[116] = "#878787", [117] = "#8787af", [118] = "#8787d7", [119] = "#8787ff",
[120] = "#87af00", [121] = "#87af5f", [122] = "#87af87", [123] = "#87afaf",
[124] = "#87afd7", [125] = "#87afff", [126] = "#87d700", [127] = "#87d75f",
[128] = "#87d787", [129] = "#87d7af", [130] = "#87d7d7", [131] = "#87d7ff",
[132] = "#87ff00", [133] = "#87ff5f", [134] = "#87ff87", [135] = "#87ffaf",
[136] = "#87ffd7", [137] = "#87ffff", [138] = "#af0000", [139] = "#af005f",
[140] = "#af0087", [141] = "#af00af", [142] = "#af00d7", [143] = "#af00ff",
[144] = "#af5f00", [145] = "#af5f5f", [146] = "#af5f87", [147] = "#af5faf",
[148] = "#af5fd7", [149] = "#af5fff", [150] = "#af8700", [151] = "#af875f",
[152] = "#af8787", [153] = "#af87af", [154] = "#af87d7", [155] = "#af87ff",
[156] = "#afaf00", [157] = "#afaf5f", [158] = "#afaf87", [159] = "#afafaf",
[160] = "#afafd7", [161] = "#afafff", [162] = "#afd700", [163] = "#afd75f",
[164] = "#afd787", [165] = "#afd7af", [166] = "#afd7d7", [167] = "#afd7ff",
[168] = "#afff00", [169] = "#afff5f", [170] = "#afff87", [171] = "#afffaf",
[172] = "#afffd7", [173] = "#afffff", [174] = "#d70000", [175] = "#d7005f",
[176] = "#d70087", [177] = "#d700af", [178] = "#d700d7", [179] = "#d700ff",
[180] = "#d75f00", [181] = "#d75f5f", [182] = "#d75f87", [183] = "#d75faf",
[184] = "#d75fd7", [185] = "#d75fff", [186] = "#d78700", [187] = "#d7875f",
[188] = "#d78787", [189] = "#d787af", [190] = "#d787d7", [191] = "#d787ff",
[192] = "#d7af00", [193] = "#d7af5f", [194] = "#d7af87", [195] = "#d7afaf",
[196] = "#d7afd7", [197] = "#d7afff", [198] = "#d7d700", [199] = "#d7d75f",
[200] = "#d7d787", [201] = "#d7d7af", [202] = "#d7d7d7", [203] = "#d7d7ff",
[204] = "#d7ff00", [205] = "#d7ff5f", [206] = "#d7ff87", [207] = "#d7ffaf",
[208] = "#d7ffd7", [209] = "#d7ffff", [210] = "#ff0000", [211] = "#ff005f",
[212] = "#ff0087", [213] = "#ff00af", [214] = "#ff00d7", [215] = "#ff00ff",
[216] = "#ff5f00", [217] = "#ff5f5f", [218] = "#ff5f87", [219] = "#ff5faf",
[220] = "#ff5fd7", [221] = "#ff5fff", [222] = "#ff8700", [223] = "#ff875f",
[224] = "#ff8787", [225] = "#ff87af", [226] = "#ff87d7", [227] = "#ff87ff",
[228] = "#ffaf00", [229] = "#ffaf5f", [230] = "#ffaf87", [231] = "#ffafaf",
[232] = "#ffafd7", [233] = "#ffafff", [234] = "#ffd700", [235] = "#ffd75f",
[236] = "#ffd787", [237] = "#ffd7af", [238] = "#ffd7d7", [239] = "#ffd7ff",
[240] = "#ffff00", [241] = "#ffff5f", [242] = "#ffff87", [243] = "#ffffaf",
[244] = "#ffffd7", [245] = "#ffffff", [246] = "#000000", [247] = "#00005f",
[248] = "#000087", [249] = "#0000af", [250] = "#0000d7", [251] = "#0000ff",
[252] = "#005f00", [253] = "#005f5f", [254] = "#005f87", [255] = "#005faf",
}
local function sanitizeRichText(text)
local replace = {
['<'] = '<',
['>'] = '>',
['"'] = '"',
["'"] = ''',
['&'] = '&'
}
local res = string.gsub(text, "([<>'\"\&])", replace)
return res
end
function term.parsecolors(text)
local richText = ""
local i = 1
while i <= #text do
if text:sub(i, i) == "\27" then
local codeStart = i + 1
local codeEnd = text:find("m", codeStart)
if codeEnd then
local code = tonumber(text:sub(codeStart, codeEnd - 1))
if code then
if code >= 0 then
richText = richText .. string.format("<font color=\"%s\">", term.colors[code])
elseif code == 0 then
richText = richText .. "</font>"
end
i = codeEnd
end
end
else
richText = richText .. text:sub(i, i)
end
i = i + 1
end
return richText
end
function term.parse(commandString, cfilesystem, self)
local command, args = parseCommand(commandString)
if not command then return end
local parsedArgs = parseOptions(args)
local path = filesystem.join("/usr/bin", command)
local exists, content = filesystem.read(fs, path)
if exists then
local lenv = getfenv()
lenv.args = args
lenv.options = parsedArgs
lenv.filesystem = cfilesystem
lenv.http = HttpService
lenv.env = setmetatable({}, {
__index = function(_, index)
return env[index]
end,
__newindex = function(_, index, value)
env[index] = value
end,
__tostring = "env"
})
if self then
lenv.self = self
lenv.cd = function(path)
path = filesystem.join(self.dir, path)
path = filesystem.parsecontext(path, self.home, self.dir)
self.dir = path
end
end
local extname = filesystem.basename(path):split(".")
extname = extname[#extname]
local success, commandsuccess, commandresponse = pcall(function()
local func
if extname == "luau" or extname == "lua" then
func = loadstring(content)
func = setfenv(func, lenv)
else
func = fiu.luau_load(content, lenv)
end
return func()
end)
if not success then
return commandsuccess
end
if commandsuccess then
return commandresponse
else
return command .. ": ".. (commandresponse or "")
end
else
return command..": command not found"
end
end
local function getHostname()
local success, hostname = filesystem.read(fs, "/etc/hostname")
if not success then
hostname = "archlinux"
end
return hostname
end
function term:init()
self.textSize, self.updateTextSize = Roact.createBinding(28)
self.root = Roact.createRef()
self.prefix = "[%s@%s %s]$"
self.user = "root"
self.home = "/root/"
self.dir = self.home
self.lines = 0
local function parsepath(path)
return filesystem.parsecontext(path, self.home, self.dir)
end
local termfilesystem = {}
function termfilesystem.symlink(path1, path2)
return filesystem.symlink(fs, parsepath(path1), parsepath(path2))
end
function termfilesystem.metadata(path)
return filesystem.metadata(fs, parsepath(path))
end
function termfilesystem.rm(path)
return filesystem.rm(fs, parsepath(path))
end
function termfilesystem.rmdir(path)
return filesystem.rmdir(fs, parsepath(path))
end
function termfilesystem.read(path)
return filesystem.read(fs, parsepath(path))
end
function termfilesystem.mkdir(path)
return filesystem.mkdir(fs, parsepath(path))
end
function termfilesystem.write(path, ...)
return filesystem.write(fs, parsepath(path), ...)
end
function termfilesystem.exists(path)
return filesystem.exists(fs, parsepath(path))
end
function termfilesystem.readdir(path)
return filesystem.readdir(fs, parsepath(path))
end
setmetatable(termfilesystem, filesystem)
self.filesystem = termfilesystem
end
function term:clear()
self.lines = 0
for _, object in self.root:getValue():GetChildren() do
if object:IsA("TextBox") then
object:Destroy()
end
end
end
function term:print(content)
local content, update = Roact.createBinding(content)
local ref = Roact.createRef()
local text = Roact.createElement("TextBox", {
Size = Roact.joinBindings({self.textSize, content}):map(function(values)
local size, content = unpack(values)
local _, lineCount = content:gsub("\n", "\n")
lineCount += 1
return UDim2.new(1, 0, 0, size * lineCount)
end),
TextSize = self.textSize,
BackgroundTransparency = 1,
Font = Enum.Font.Code,
TextColor3 = Color3.new(1, 1, 1),
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Top,
Text = content,
TextEditable = false,
ClearTextOnFocus = false,
MultiLine = false,
[Roact.Ref] = ref
})
self.lines += 1
Roact.mount(text, self.root:getValue())
local object = ref:getValue()
object:SetAttribute("line", self.lines)
return {
content = content,
update = update,
object = object
}
end
function term:newline(initial, prefix)
local dirname = filesystem.basename(self.dir)
if filesystem.join(self.dir) == filesystem.join(self.home) then
dirname = "~"
end
if #dirname == 0 then
dirname = "/"
end
local prefix = self.prefix:format(self.user, getHostname(), dirname)
local line = self:print(prefix .. " " .. (initial or ""))
line.object:SetAttribute("start", #prefix + 2)
local update = line.update
line.update = function(content)
return update(prefix .. " " .. content)
end
return line
end
function term:didMount()
local sg = Instance.new("ScreenGui")
sg.ResetOnSpawn = false
sg.Parent = owner.PlayerGui
local remote = Instance.new("RemoteEvent")
remote.Parent = sg
NLS([[local remote, root = ...
local sub = string.sub
local uis = game:GetService("UserInputService")
local lines = {}
local currentline
local text = ""
local focused = false
uis.InputBegan:Connect(function(input, gp)
if input.KeyCode == Enum.KeyCode.Return and focused then
focused = false
lines[currentline]:ReleaseFocus()
remote:FireServer("return", text)
end
end)
local inputs = {}
function killinputs()
for _, connection in pairs(inputs) do
if typeof(connection) == "function" then
connection()
else
connection:Disconnect()
end
end
inputs = {}
end
function inputlisten(line)
killinputs()
local fake = line:Clone()
fake.TextTransparency = 0.5
fake:SetAttribute("line", nil)
fake:SetAttribute("start", nil)
fake.Size = UDim2.fromScale(1, 1)
fake.ZIndex = 2
fake.Name = "input"
fake.TextEditable = true
fake.Parent = line
local start = line:GetAttribute("start")
local prefix = sub(line.Text, 1, start - 2)
text = sub(line.Text, start)
table.insert(inputs, fake:GetPropertyChangedSignal("Text"):Connect(function()
local new = fake.Text
if sub(new, 1, start - 1) ~= prefix .. " " then
fake.Text = prefix .. " "
end
text = sub(fake.Text, start)
remote:FireServer("update", text)
end))
table.insert(inputs, fake.Focused:Connect(function()
focused = true
end))
table.insert(inputs, fake.FocusLost:Connect(function()
task.wait()
focused = false
end))
table.insert(inputs, function()
fake:Destroy()
focused = false
text = ""
end)
end
function addline(line)
local lineValue = line:GetAttribute("line")
if lineValue and line:GetAttribute("start") then
lines[lineValue] = line
if lineValue > (currentline or 0) then
currentline = lineValue
inputlisten(line)
end
line:GetPropertyChangedSignal("Parent"):Connect(function()
if line.Parent == nil then
lines[lineValue] = nil
if currentline == lineValue then
currentline = nil
killinputs()
end
end
end)
end
end
for _, line in pairs(root:GetChildren()) do
addline(line)
end
root.ChildAdded:Connect(addline)
root.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
if currentline then
lines[currentline].input:CaptureFocus()
end
end
end)]], sg, remote, self.root:getValue())
local current = self:newline()
remote.OnServerEvent:Connect(function(sender, action, data)
if sender ~= owner then return end
if action == "update" then
current.update(data)
elseif action == "return" then
local result = term.parse(data, self.filesystem, self)
if result then
self:print(term.parsecolors(sanitizeRichText(tostring(result))))
end
current.update(data)
current = self:newline()
end
end)
end
function term:render()
local root = Roact.createElement("Frame", {
Size = FULLSCALE,
BackgroundColor3 = Color3.new(),
BorderSizePixel = 0,
ZIndex = 0,
[Roact.Ref] = self.root
}, {
list = Roact.createElement("UIListLayout")
})
return root
end
end
function linux:init()
end
function linux:render()
local root = Roact.createElement("Frame", {
Size = FULLSCALE,
BackgroundColor3 = Color3.new(),
BorderSizePixel = 0,
ZIndex = 0,
}, {
term = Roact.createElement(term)
})
return root
end
end
local gui, screen do
local resolution = Vector2.new(1920, 1080)
local screenSize = 7
screen = Instance.new("Part")
screen.CanCollide = false
screen.CanTouch = false
screen.Locked = true
screen.Name = "Base"
screen.Size = Vector3.new(screenSize * (resolution.X / resolution.Y), screenSize, 0)
screen.Anchored = true
screen.Parent = script
task.spawn(function()
while true do
task.wait()
local char = owner.Character
if char then
local hrp = char:FindFirstChild("HumanoidRootPart")
if hrp then
screen.CFrame = hrp.CFrame * CFrame.new(0, screen.Size.Y / 2, -5)
end
end
end
end)
gui = Roact.createElement("SurfaceGui", {
Face = Enum.NormalId.Back,
SizingMode = Enum.SurfaceGuiSizingMode.FixedSize,
CanvasSize = resolution,
ClipsDescendants = true,
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
LightInfluence = 0,
}, {
content = Roact.createElement(linux)
})
end
Roact.mount(gui, screen)