PRE-ALPHA 3.0.0 - Rewrote the kernel to use a more modular design, changed some terms, added process sandboxing for security.

COMING IN THE FULL RELEASE:
- A user system
- A functional IPC (Inter-Process Communication) system

THINGS CAN AND WILL CHANGE FROM NOW UNTIL THE FINAL RELEASE.
This commit is contained in:
WahPlus
2025-08-17 16:38:08 +03:00
parent fdc54a8839
commit cbf25999f0
52 changed files with 1232 additions and 955 deletions
-97
View File
@@ -1,97 +0,0 @@
local compLib
local LLcomponent
if table.copy then
compLib = table.copy(component)
LLcomponent = table.copy(component)
else
compLib = {}
LLcomponent = component
end
--local ocelot = LLcomponent.proxy(LLcomponent.list("ocelot")())
--ocelot.log("loaded")
_G.componentlib = {["additions"] = {}, ["removals"] = {}}
compLib.virtual = {}
function compLib.virtual.add(address, componentType, proxy)
checkArg(1, address, "string")
checkArg(2, componentType, "string")
checkArg(3, proxy, "table")
proxy["address"] = address
componentlib.additions[address] = {["componentType"] = componentType, ["proxy"] = proxy}
if componentlib.removals[address] then
componentlib.removals[address] = nil
end
end
function compLib.virtual.remove(address)
checkArg(1, address, "string")
if componentlib.additions[address] then
componentlib.additions[address] = nil
else
table.insert(componentlib.removals, address)
end
end
function compLib.list(componentType)
checkArg(1, componentType, "string", "nil")
local componentList = table.copy(LLcomponent.list(componentType))
for address, dataTable in pairs(componentlib.additions) do
if dataTable.componentType == componentType or not componentType then
componentList[address] = dataTable.componentType
end
end
for _, address in pairs(componentlib.removals) do
componentList[address] = nil
end
local i, value
setmetatable(componentList, {__call = function(self)
i, value = next(self, i)
return i, value
end})
return componentList
end
function compLib.proxy(address)
if componentlib.additions[address] then
--ocelot.log("vcomponent")
return componentlib.additions[address].proxy
else
return LLcomponent.proxy(address)
end
end
function compLib.invoke(address, funcName, ...)
--ocelot.log("Invoking " .. funcName .. " from " .. address)
if componentlib.additions[address] then
--ocelot.log("vcomponent")
return componentlib.additions[address].proxy[funcName](...)
else
return LLcomponent.invoke(address, funcName, ...)
end
end
function compLib.get(address)
checkArg(1, address, "string")
if #address < 3 then
return nil, "abbreviated address must be at least 3 characters long"
end
for currentAddress, name in compLib.list() do
if currentAddress:find("^" .. address) then
return(currentAddress)
end
end
return nil, "full address not found"
end
-- Add main component proxies to the library
setmetatable(compLib, {["__index"] = function(_, item)
if compLib.list(item)() then
return compLib.proxy(compLib.list(item)())
else
return compLib[item]
end
end})
return compLib
-14
View File
@@ -1,14 +0,0 @@
local computerlib = table.copy(computer)
local LLcomputer = table.copy(computer)
function computerlib.pullSignal(timeout)
local startTime = LLcomputer.uptime()
local result
repeat
result = {LLcomputer.pullSignal(0)}
coroutine.yield()
until result or timeout and LLcomputer.uptime() >= startTime + timeout
return table.unpack(result)
end
return computerlib
-55
View File
@@ -1,55 +0,0 @@
local computer = import("computer")
local event = {}
local bufferTime = 0.1 -- A little bit of buffer time so events won't be skipped by accident.
--local ocelot = component.proxy(component.list("ocelot")())
function event.pull(...)
local args = {...}
local evtypes, timeout = {}, nil
for _, arg in pairs(args) do
if type(arg) == "number" and not timeout then -- It's a timeout
timeout = arg
else -- It's an event type
table.insert(evtypes, tostring(arg))
end
end
local startTime = computer.uptime()
while true do
-- Check event queue for matching event
for i = 1, #evmgr.eventQueue do
local foundevent = false
if evtypes[1] then -- event type(s) specified
for _, evtype in pairs(evtypes) do
if evmgr.eventQueue[i][2] == evtype and evmgr.eventQueue[i][1] >= startTime - bufferTime then
foundevent = true
end
end
else
if evmgr.eventQueue[i][1] >= startTime - bufferTime then
foundevent = true
end
end
if foundevent then
-- Found matching event (or any event if no type specified)
local result = table.copy(evmgr.eventQueue[i])
table.remove(evmgr.eventQueue, i)
table.remove(result, 1) -- remove the time of event argument
return table.unpack(result)
end
end
-- Check if we've timed out
if timeout and computer.uptime() >= startTime + timeout then
return nil -- Timed out, return nil
end
-- Yield to allow other processes to run and more events to be added
coroutine.yield()
end
end
return event
-506
View File
@@ -1,506 +0,0 @@
local loadfile = ... -- raw loadfile from boot.lua
local component, computer
if loadfile then
unicode = loadfile("/halyde/lib/unicode.lua")(loadfile)
component = loadfile("/halyde/lib/component.lua")(loadfile)
computer = _G.computer
elseif import then
unicode = import("unicode")
component = import("component")
computer = import("computer")
end
local filesystem = {}
function filesystem.canonical(path)
checkArg(1, path, "string")
local segList = {}
if path:sub(1, 1) ~= "/" then
path = "/" .. path
end
path = path:gsub("/+", "/")
for segment in path:gmatch("[^/]+") do
if segment == ".." and segList[1] then
table.remove(segList, #segList)
elseif segment ~= "." then
table.insert(segList, segment)
end
end
return "/" .. table.concat(segList, "/")
end
function filesystem.concat(path1, path2)
checkArg(1, path1, "string")
checkArg(2, path2, "string")
if path1:sub(-1, -1) == "/" then
path1 = path1:sub(1, -2)
end
if path2:sub(1, 1) ~= "/" then
path2 = "/" .. path2
end
return path1 .. path2
end
function filesystem.absolutePath(path) -- returns the address and absolute path of an object
checkArg(1, path, "string")
path = filesystem.canonical(path)
local address = nil
if path:find("^/tmp") then
address = computer.tmpAddress()
path = path:sub(5)
elseif path:find("^/mnt/...") then
address = component.get(path:sub(6,8))
if not address then
address = computer.getBootAddress()
else
path = path:sub(9)
end
else
address = computer.getBootAddress()
end
if not address then
return nil, "no such component"
end
return address, path
end
function filesystem.exists(path) -- check if path exists
checkArg(1, path, "string")
local address, absPath = filesystem.absolutePath(path)
if not address then
return false
end
if absPath:find("^/special/drive/...") then
return not not (computer.getBootAddress() and component.get(absPath:sub(16,18)))
end
if absPath:find("^/special/eeprom/") then
return table.find({"init.lua","data.bin","label.txt"},absPath:sub(17))
end
return component.invoke(address, "exists", absPath)
end
local function readBytes(self,n)
n = n or 1
if n==1 then
local byte = self:read(1)
if byte==nil then return nil end
return string.byte(byte)
end
local bytes, res = {string.byte(self:read(n),1,n)}, 0
if self.littleEndian then
for i=#bytes,1,-1 do
res = (res<<8)&0xFFFFFFFF | bytes[i]
end
else
for i=1,#bytes do
res = (res<<8)&0xFFFFFFFF | bytes[i]
end
end
return res
end
local function readUnicodeChar(self)
return unicode.readChar(function()
return self:readBytes(1)
end)
end
local function iterateBytes(self)
return function()
local byte = readBytes(self,1)
if byte==nil then self:close() end
return byte
end
end
local function iterateUnicodeChars(self)
return unicode.iterate(iterateBytes(self))
end
function filesystem.makeReadStream(content)
local properHandle = {}
local readcursor = 1
function properHandle.read(self, amount)
checkArg(2, amount, "number")
local limit = string.len(content)+1
local out = nil
if readcursor<limit then
if amount==math.huge then
out = string.sub(content,math.min(readcursor,limit))
else
out = string.sub(content,math.min(readcursor,limit),math.min(readcursor+amount-1,limit))
end
end
readcursor=readcursor+amount
if out=="" then
return nil
end
return out
end
properHandle.readBytes = readBytes
properHandle.readUnicodeChar = readUnicodeChar
properHandle.iterateBytes = iterateBytes
properHandle.iterateUnicodeChars = iterateUnicodeChars
function properHandle.write()
return nil
end
function properHandle.close()
content=nil
end
return properHandle
end
function filesystem.open(path, mode, buffered) -- opens a file and returns its handle
checkArg(1, path, "string")
checkArg(2, mode, "string", "nil")
checkArg(3, buffered, "boolean", "nil")
if not mode then
mode = "r"
end
if buffered == nil then
buffered = true
end
if not (mode == "r" or mode == "w" or mode == "rb" or mode == "wb" or mode == "a" or mode == "ab") then
return nil, "invalid handle type"
end
if path:find("^/special") and not filesystem.exists(path) then
return nil, "/special does not allow creating files"
end
local address, absPath = filesystem.absolutePath(path)
local unmanagedDrive = address==computer.getBootAddress() and absPath:find("^/special/drive")
local unmanagedProxy, sectorSize, sectorCount, handle
if unmanagedDrive then
unmanagedProxy = component.proxy(component.get(absPath:sub(16,18)))
sectorSize = unmanagedProxy.getSectorSize()
sectorCount = math.ceil(unmanagedProxy.getCapacity()/sectorSize)
elseif not (address==computer.getBootAddress() and absPath:find("^/special/")) then
local handleArgs = {component.invoke(address, "open", absPath, mode)}
handle = handleArgs[1]
if not handle then
return table.unpack(handleArgs)
end
handleArgs = nil
end
local properHandle = {}
properHandle.handle = handle
properHandle.address = address
local content = nil
local readcursor = 1
if buffered and mode:sub(1,1)=="r" then
content=""
repeat
tmpdata = component.invoke(address, "read", handle, math.huge or math.maxinteger)
content = content .. (tmpdata or "")
until not tmpdata
component.invoke(address, "close", handle)
end
function properHandle.read(self, amount)
checkArg(2, amount, "number")
if unmanagedDrive then
local sectorIdx = ((readcursor-1)//sectorSize)+1
if sectorIdx>sectorCount then return nil end
local sector = unmanagedProxy.readSector(sectorIdx)
local data = sector:sub(((readcursor-1)%sectorSize)+1,((readcursor+math.min(amount,sectorSize)-2)%sectorSize)+1)
readcursor=readcursor+#data
if data=="" then return nil end
return data
else
if buffered then
local limit = string.len(content)+1
local out = nil
if readcursor<limit then
if amount==math.huge then
out = string.sub(content,math.min(readcursor,limit))
else
out = string.sub(content,math.min(readcursor,limit),math.min(readcursor+amount-1,limit))
end
end
readcursor=readcursor+amount
if out=="" then
return nil
end
return out
else
return component.invoke(self.address, "read", self.handle, amount)
end
end
end
properHandle.readBytes = readBytes
properHandle.readUnicodeChar = readUnicodeChar
properHandle.iterateBytes = iterateBytes
properHandle.iterateUnicodeChars = iterateUnicodeChars
function properHandle.write(self, data)
checkArg(2, data, "string")
if unmanagedDrive then
local startSector = ((readcursor-1)//sectorSize)+1
if startSector>sectorCount then return nil, "not enough space" end
local startSByte = ((readcursor-1)%sectorSize)+1
local sect = unmanagedProxy.readSector(startSector)
unmanagedProxy.writeSector(startSector,sect:sub(1,startSByte-1)..data:sub(1,sectorSize-startSByte+1))
for i=2,(#data+startSByte)//sectorSize do
if startSector+i-1>sectorCount then return nil, "not enough space" end
unmanagedProxy.writeSector(startSector+i-1,data:sub(startSByte+sectorSize*(i-1),startSByte+sectorSize*i-1))
end
readcursor=readcursor+#data
return true
else
return component.invoke(self.address, "write", self.handle, data)
end
end
function properHandle.close(self)
if buffered then
content = nil
else
return component.invoke(self.address, "close", self.handle)
end
end
if address==computer.getBootAddress() then
local eeprom
pcall(function()
eeprom = component.eeprom
end)
if eeprom then
local getFunc, setFunc
if absPath=="/special/eeprom/init.lua" then
getFunc,setFunc = "get","set"
elseif absPath=="/special/eeprom/data.bin" then
getFunc,setFunc = "getData","setData"
elseif absPath=="/special/eeprom/label.txt" then
getFunc,setFunc = "getLabel","setLabel"
end
if mode:sub(1,1)=="r" and getFunc then
local stream = filesystem.makeReadStream(eeprom[getFunc]() or "")
properHandle.read = stream.read
properHandle.close = stream.close
elseif mode:sub(1,1)=="w" and setFunc then
local content = ""
function properHandle.write(self, data)
checkArg(2, data, "string")
content=content..data
end
function properHandle.close(self)
return eeprom[setFunc](content)
end
end
end
end
return properHandle
end
function filesystem.list(path)
checkArg(1, path, "string")
path = filesystem.canonical(path)
if path == "/mnt" then
-- list drives
local returnTable = {}
local tmpAddress = computer.tmpAddress()
for address, _ in component.list("filesystem") do
if address~=tmpAddress then
table.insert(returnTable, address:sub(1, 3) .. "/")
end
end
return returnTable
elseif path == "/special/drive" then
local returnTable = {}
local tmpAddress = computer.tmpAddress()
for address, type in component.list("drive") do
if address~=tmpAddress and type=="drive" then
table.insert(returnTable, address:sub(1, 3))
end
end
return returnTable
elseif path=="/special/eeprom" then
return {"init.lua","data.bin","label.txt"}
else
local address, absPath = filesystem.absolutePath(path)
if not address then
return false
end
return component.invoke(address, "list", absPath)
end
end
function filesystem.size(path)
checkArg(1, path, "string")
local address, absPath = filesystem.absolutePath(path)
if not address then
return false
end
if address==computer.getBootAddress() then
if absPath:find("^/special/drive") then
local drive = component.get(absPath:sub(16,18))
if not drive then return false end
return component.invoke(drive,"getCapacity")
elseif absPath:find("^/special/eeprom") then
local eeprom
pcall(function()
eeprom = component.eeprom
end)
if eeprom then
local getFunc
if absPath=="/special/eeprom/init.lua" then
getFunc = "get"
elseif absPath=="/special/eeprom/data.bin" then
getFunc = "getData"
elseif absPath=="/special/eeprom/label.txt" then
getFunc = "getLabel"
end
return #(eeprom[getFunc]())
end
end
end
return component.invoke(address, "size", absPath)
end
local function getRecursiveList(address,absPath)
local list = component.invoke(address,"list",absPath)
local dirList = {}
local listChanged = true
while listChanged do
listChanged = false
for i=1,#list do
if component.invoke(address, "isDirectory", absPath.."/"..list[i]) then
listChanged = true
local dir = list[i]
if dir:sub(-1)=="/" then dir=dir:sub(1,-2) end
table.insert(dirList,dir)
table.remove(list,i)
local subDir = component.invoke(address,"list",absPath.."/"..dir)
for j=1,#subDir do table.insert(list,dir.."/"..subDir[j]) end
end
end
end
return list,dirList
end
local function copyContent(fromHandle,toHandle)
if not (fromHandle and toHandle) then return end
local memory = math.floor(computer.freeMemory()*0.8)
local tmpdata
while true do
tmpdata = fromHandle:read(memory)
if not tmpdata then break end
local status,reason = toHandle:write(tmpdata)
if status~=true then break end
end
fromHandle:close()
toHandle:close()
end
local function copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
-- TODO: make this use copyContent
if fromAbsPath:sub(-1)=="/" then fromAbsPath=fromAbsPath:sub(1,-2) end
if toAbsPath:sub(-1)=="/" then toAbsPath=toAbsPath:sub(1,-2) end
component.invoke(toAddress,"makeDirectory",toAbsPath)
local fileList,dirList = getRecursiveList(fromAddress,fromAbsPath)
for i=1,#dirList do
component.invoke(toAddress,"makeDirectory",toAbsPath.."/"..dirList[i])
end
for i=1,#fileList do
local fromFile, toFile = fromAbsPath.."/"..fileList[i], toAbsPath.."/"..fileList[i]
--[[ local handle = component.invoke(fromAddress, "open", fromFile, "r")
local data, tmpdata = "", nil
repeat
tmpdata = component.invoke(fromAddress, "read", handle, math.huge or math.maxinteger)
data = data .. (tmpdata or "")
until not tmpdata
tmpdata = component.invoke(fromAddress, "close", handle)
local handle = component.invoke(toAddress, "open", toFile, "w")
component.invoke(toAddress, "write", handle, data)
component.invoke(toAddress, "close", handle) ]]
local fromHandle = component.invoke(fromAddress, "open", fromFile, "r")
local toHandle = component.invoke(toAddress, "open", toFile, "w")
copyContent({
["read"]=function(...) return component.invoke(fromAddress, "read", handle, ...) end,
["close"]=function(...) return component.invoke(fromAddress, "close", handle, ...) end
},{
["write"]=function(...) return component.invoke(fromAddress, "write", handle, ...) end,
["close"]=function(...) return component.invoke(fromAddress, "close", handle, ...) end
})
end
end
function filesystem.isDirectory(path)
checkArg(1, path, "string")
local address, absPath = filesystem.absolutePath(path)
if not address then
return false
end
return component.invoke(address, "isDirectory", absPath)
end
function filesystem.rename(fromPath, toPath)
checkArg(1, fromPath, "string")
checkArg(2, toPath, "string")
local fromAddress, fromAbsPath = filesystem.absolutePath(fromPath)
local toAddress, toAbsPath = filesystem.absolutePath(toPath)
if not fromAddress or not toAddress then
return false
end
if fromAddress == toAddress then
return component.invoke(fromAddress, "rename", fromAbsPath, toAbsPath)
elseif filesystem.isDirectory(fromPath) then -- component.invoke(fromAddress, "isDirectory", fromAbsPath) then
copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
filesystem.remove(fromPath) -- component.invoke(fromAddress,"remove", fromAbsPath)
else
local handle, data, tmpdata = filesystem.open(fromPath), "", nil -- component.invoke(fromAddress, "open", fromAbsPath, "r"), "", nil
repeat
tmpdata = handle:read(math.huge or math.maxinteger) -- component.invoke(fromAddress, "read", handle, math.huge or math.maxinteger)
data = data .. (tmpdata or "")
until not tmpdata
tmpdata = handle:close() -- component.invoke(fromAddress, "close", handle)
local handle = filesystem.open(toPath) -- component.invoke(toAddress, "open", toAbsPath, "w")
handle:write(data) -- component.invoke(toAddress, "write", handle, data)
handle:close() -- component.invoke(toAddress, "close", handle)
filesystem.remove(fromPath) -- component.invoke(fromAddress, "remove", fromAbsPath)
end
end
function filesystem.copy(fromPath, toPath)
checkArg(1, fromPath, "string")
checkArg(2, toPath, "string")
local fromAddress, fromAbsPath = filesystem.absolutePath(fromPath)
local toAddress, toAbsPath = filesystem.absolutePath(toPath)
if not fromAddress or not toAddress then
return false
end
if filesystem.isDirectory(fromPath) then -- component.invoke(fromAddress, "isDirectory", fromAbsPath)
copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
else
--[[ local handle = filesystem.open(fromPath,"r")
local data, tmpdata = "", nil
repeat
tmpdata = handle:read(math.huge or math.maxinteger)
data = data .. (tmpdata or "")
until not tmpdata
tmpdata = handle:close()
local handle = filesystem.open(toPath,"w")
handle:write(data)
handle:close() ]]
copyContent(filesystem.open(fromPath,"r"),filesystem.open(toPath,"w"))
end
end
function filesystem.remove(path)
checkArg(1, path, "string")
local address, absPath = filesystem.absolutePath(path)
if not address then
return false
end
if absPath:find("^/special") then return false end
if absPath:find("^/tmp") then return false end
if absPath:find("^/mnt") then return false end
return component.invoke(address, "remove", absPath)
end
function filesystem.makeDirectory(path)
checkArg(1, path, "string")
local address, absPath = filesystem.absolutePath(path)
if not address then
return false
end
return component.invoke(address, "makeDirectory", absPath)
end
return(filesystem)
-4
View File
@@ -1,4 +0,0 @@
-- json.lua by rxi
-- Minified with luamin
-- Original: https://github.com/rxi/json.lua
local a={_version="0.1.2"}local b;local c={["\\"]="\\",["\""]="\"",["\b"]="b",["\f"]="f",["\n"]="n",["\r"]="r",["\t"]="t"}local d={["/"]="/"}for e,f in pairs(c)do d[f]=e end;local function g(h)return"\\"..(c[h]or string.format("u%04x",h:byte()))end;local function i(j)return"null"end;local function k(j,l)local m={}l=l or{}if l[j]then error("circular reference")end;l[j]=true;if rawget(j,1)~=nil or next(j)==nil then local n=0;for e in pairs(j)do if type(e)~="number"then error("invalid table: mixed or invalid key types")end;n=n+1 end;if n~=#j then error("invalid table: sparse array")end;for o,f in ipairs(j)do table.insert(m,b(f,l))end;l[j]=nil;return"["..table.concat(m,",").."]"else for e,f in pairs(j)do if type(e)~="string"then error("invalid table: mixed or invalid key types")end;table.insert(m,b(e,l)..":"..b(f,l))end;l[j]=nil;return"{"..table.concat(m,",").."}"end end;local function p(j)return'"'..j:gsub('[%z\1-\31\\"]',g)..'"'end;local function q(j)if j~=j or j<=-math.huge or j>=math.huge then error("unexpected number value '"..tostring(j).."'")end;return string.format("%.14g",j)end;local r={["nil"]=i,["table"]=k,["string"]=p,["number"]=q,["boolean"]=tostring}b=function(j,l)local s=type(j)local t=r[s]if t then return t(j,l)end;error("unexpected type '"..s.."'")end;function a.encode(j)return b(j)end;local u;local function v(...)local m={}for o=1,select("#",...)do m[select(o,...)]=true end;return m end;local w=v(" ","\t","\r","\n")local x=v(" ","\t","\r","\n","]","}",",")local y=v("\\","/",'"',"b","f","n","r","t","u")local z=v("true","false","null")local A={["true"]=true,["false"]=false,["null"]=nil}local function B(C,D,E,F)for o=D,#C do if E[C:sub(o,o)]~=F then return o end end;return#C+1 end;local function G(C,D,H)local I=1;local J=1;for o=1,D-1 do J=J+1;if C:sub(o,o)=="\n"then I=I+1;J=1 end end;error(string.format("%s at line %d col %d",H,I,J))end;local function K(n)local t=math.floor;if n<=0x7f then return string.char(n)elseif n<=0x7ff then return string.char(t(n/64)+192,n%64+128)elseif n<=0xffff then return string.char(t(n/4096)+224,t(n%4096/64)+128,n%64+128)elseif n<=0x10ffff then return string.char(t(n/262144)+240,t(n%262144/4096)+128,t(n%4096/64)+128,n%64+128)end;error(string.format("invalid unicode codepoint '%x'",n))end;local function L(M)local N=tonumber(M:sub(1,4),16)local O=tonumber(M:sub(7,10),16)if O then return K((N-0xd800)*0x400+O-0xdc00+0x10000)else return K(N)end end;local function P(C,o)local m=""local Q=o+1;local e=Q;while Q<=#C do local R=C:byte(Q)if R<32 then G(C,Q,"control character in string")elseif R==92 then m=m..C:sub(e,Q-1)Q=Q+1;local h=C:sub(Q,Q)if h=="u"then local S=C:match("^[dD][89aAbB]%x%x\\u%x%x%x%x",Q+1)or C:match("^%x%x%x%x",Q+1)or G(C,Q-1,"invalid unicode escape in string")m=m..L(S)Q=Q+#S else if not y[h]then G(C,Q-1,"invalid escape char '"..h.."' in string")end;m=m..d[h]end;e=Q+1 elseif R==34 then m=m..C:sub(e,Q-1)return m,Q+1 end;Q=Q+1 end;G(C,o,"expected closing quote for string")end;local function T(C,o)local R=B(C,o,x)local M=C:sub(o,R-1)local n=tonumber(M)if not n then G(C,o,"invalid number '"..M.."'")end;return n,R end;local function U(C,o)local R=B(C,o,x)local V=C:sub(o,R-1)if not z[V]then G(C,o,"invalid literal '"..V.."'")end;return A[V],R end;local function W(C,o)local m={}local n=1;o=o+1;while 1 do local R;o=B(C,o,w,true)if C:sub(o,o)=="]"then o=o+1;break end;R,o=u(C,o)m[n]=R;n=n+1;o=B(C,o,w,true)local X=C:sub(o,o)o=o+1;if X=="]"then break end;if X~=","then G(C,o,"expected ']' or ','")end end;return m,o end;local function Y(C,o)local m={}o=o+1;while 1 do local Z,j;o=B(C,o,w,true)if C:sub(o,o)=="}"then o=o+1;break end;if C:sub(o,o)~='"'then G(C,o,"expected string for key")end;Z,o=u(C,o)o=B(C,o,w,true)if C:sub(o,o)~=":"then G(C,o,"expected ':' after key")end;o=B(C,o+1,w,true)j,o=u(C,o)m[Z]=j;o=B(C,o,w,true)local X=C:sub(o,o)o=o+1;if X=="}"then break end;if X~=","then G(C,o,"expected '}' or ','")end end;return m,o end;local _={['"']=P,["0"]=T,["1"]=T,["2"]=T,["3"]=T,["4"]=T,["5"]=T,["6"]=T,["7"]=T,["8"]=T,["9"]=T,["-"]=T,["t"]=U,["f"]=U,["n"]=U,["["]=W,["{"]=Y}u=function(C,D)local X=C:sub(D,D)local t=_[X]if t then return t(C,D)end;G(C,D,"unexpected character '"..X.."'")end;function a.decode(C)if type(C)~="string"then error("expected argument of type string, got "..type(C))end;local m,D=u(C,B(C,1,w,true))D=B(C,D,w,true)if D<=#C then G(C,D,"trailing garbage")end;return m end;return a
-432
View File
@@ -1,432 +0,0 @@
local raster = {
["units"]={},
["defaultBackgroundColor"]=0x000000,
["defaultForegroundColor"]=0xFFFFFF,
["displayWidth"]=0,
["displayHeight"]=0,
["charWidth"]=0,
["charHeight"]=0,
["backgroundColor"]=0xFFFFFF
}
local component = import("component")
-- local ocelot = component.proxy(component.list("ocelot")())
local gpu = component.gpu
local display = {}
local chunksAffected = {}
local renderBuffer = nil
-- braille rendering
function raster.units.charToBraille(x,y)
return x*2,y*4
end
function raster.units.brailleToChar(x,y)
return math.ceil(x/2),math.ceil(y/4)
end
function raster.init(width, height, bgcolor)
-- NOTE: Width and height are in characters, not pixels in braille.
-- If the width and height are nil, the entire screen will be used.
if width==nil and height==nil then
width, height = gpu.getResolution()
end
raster.charWidth = width
raster.charHeight = height
width, height = raster.units.charToBraille(width, height)
bgcolor = bgcolor or raster.defaultBackgroundColor
if bgcolor~=0 then
for i=1,width*height do
display[i]=bgcolor
end
end
raster.displayWidth = width
raster.displayHeight = height
raster.backgroundColor = bgcolor
pcall(function()
renderBuffer = gpu.allocateBuffer()
end)
raster.clear()
end
function raster.set(x, y, color)
if x<1 or x>raster.displayWidth or y<1 or y>raster.displayHeight then
return false
end
color = color or raster.defaultForegroundColor
local i = x+y*raster.displayWidth
display[i] = color
local ci = math.floor((x-1)/2)+math.floor((y-1)/4)*raster.charWidth+1
-- ocelot.log(x..","..y..":"..ci)
chunksAffected[ci] = true
return true
end
function raster.get(x, y)
local i = x+y*raster.displayWidth
return display[i] or raster.backgroundColor
end
local function stats(arr)
local out = {}
for i=1,#arr do
local v = arr[i]
if out[v]==nil then
out[v]=1
else
out[v] = out[v] + 1
end
end
return out
end
local function getKeys(t)
local keys = {}
for k,v in pairs(t) do
table.insert(keys,{k,v})
end
table.sort(keys,function(a,b)
return a[2]>b[2]
end)
for i=1,#keys do
keys[i] = keys[i][1]
end
return keys
end
local function colorDifference(a,b)
return ((a>>16)&255)-((b>>16)&255)+((a>>8)&255)-((b>>8)&255)+(a&255)-(b&255)
end
local function limitTwoColors(arr)
local colors = getKeys(stats(arr))
for i=1,#arr do
local v=arr[i]
if v==colors[1] then
arr[i]=0
goto continue
elseif v==colors[2] then
arr[i]=1
goto continue
else
--error("Pixel is not in the two colors (raster.lua:90)")
-- get closest color so atleast it kinda shows
if colorDifference(v,colors[1])<colorDifference(v,colors[2]) then
arr[i]=0
else
arr[i]=1
end
end
::continue::
end
return arr,colors[1] or 0,colors[2] or 0
end
local function arrayToBraille(arr)
local codePoint = 0x2800
for i=1,8 do
codePoint = codePoint | arr[i]<<(i-1)
end
if codePoint==0x2800 then return " " end
return utf8.char(codePoint)
end
function raster.update()
if renderBuffer~=nil then
gpu.setActiveBuffer(renderBuffer)
end
for y=1,raster.displayHeight,4 do
-- gpu.set(0,0,tostring(y))
for x=1,raster.displayWidth,2 do
local ci = math.floor(x/2)+math.floor(y/4)*raster.charWidth+1
if chunksAffected[ci] then
local chunk = {
raster.get(x,y),
raster.get(x,y+1),
raster.get(x,y+2),
raster.get(x+1,y),
raster.get(x+1,y+1),
raster.get(x+1,y+2),
raster.get(x,y+3),
raster.get(x+1,y+3)
}
local colorA = nil
local colorB = nil
chunk,colorA,colorB = limitTwoColors(chunk)
-- print(tostring(colorA)..","..tostring(colorB))
cx,cy=raster.units.brailleToChar(x,y)
gpu.setBackground(colorA)
gpu.setForeground(colorB)
-- gpu.set(cx,cy,tostring(colorB/0xFFFFFF))
gpu.set(cx,cy,arrayToBraille(chunk))
chunksAffected[ci] = false
end
end
end
if renderBuffer~=nil then
gpu.bitblt()
gpu.setActiveBuffer(0)
end
end
function raster.clear()
if renderBuffer~=nil then
gpu.setActiveBuffer(renderBuffer)
end
-- clear()
local bgcolor = raster.backgroundColor
gpu.setBackground(bgcolor)
gpu.fill(1,1,raster.displayWidth,raster.displayHeight," ")
display = {}
end
function raster.free()
if renderBuffer==nil then
return true
else
return gpu.freeBuffer(renderBuffer)
end
end
-- advanced rendering
function raster.drawLine(x1, y1, x2, y2, color)
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
local dx = math.abs(x2 - x1)
local dy = math.abs(y2 - y1)
local sx = x1 < x2 and 1 or -1
local sy = y1 < y2 and 1 or -1
local err = dx - dy
while true do
raster.set(x1, y1, color)
if x1 == x2 and y1 == y2 then
break
end
local e2 = 2 * err
if e2 > -dy then
err = err - dy
x1 = x1 + sx
end
if e2 < dx then
err = err + dx
y1 = y1 + sy
end
end
end
function raster.drawRect(x1,y1,x2,y2,col)
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
if x1 > x2 then x1, x2 = x2, x1 end
if y1 > y2 then y1, y2 = y2, y1 end
for x=x1,x2 do
raster.set(x,y1,col)
raster.set(x,y2,col)
end
for y=y1+1,y2-1 do
raster.set(x1,y,col)
raster.set(x2,y,col)
end
end
function raster.fillRect(x1,y1,x2,y2,col)
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
if x1 > x2 then x1, x2 = x2, x1 end
if y1 > y2 then y1, y2 = y2, y1 end
for x=x1,x2 do
for y=y1,y2 do
raster.set(x,y,col)
end
end
end
function raster.drawCircle(xc, yc, radius, color)
xc=math.floor(xc)
yc=math.floor(yc)
radius=math.floor(radius)
local x = 0
local y = radius
local d = 3 - 2 * radius
while y >= x do
-- Draw 8 symmetric points
raster.set(xc + x, yc + y, color)
raster.set(xc - x, yc + y, color)
raster.set(xc + x, yc - y, color)
raster.set(xc - x, yc - y, color)
raster.set(xc + y, yc + x, color)
raster.set(xc - y, yc + x, color)
raster.set(xc + y, yc - x, color)
raster.set(xc - y, yc - x, color)
if d < 0 then
d = d + 4 * x + 6
else
d = d + 4 * (x - y) + 10
y = y - 1
end
x = x + 1
end
end
function raster.drawEllipse(x1, y1, x2, y2, color)
if x1 > x2 then x1, x2 = x2, x1 end
if y1 > y2 then y1, y2 = y2, y1 end
local xc = math.floor((x1 + x2) / 2)
local yc = math.floor((y1 + y2) / 2)
local a = math.floor((x2 - x1) / 2)
local b = math.floor((y2 - y1) / 2)
if a <= 0 or b <= 0 then
return
end
if a == b then
raster.drawCircle(xc, yc, a, color)
return
end
if a <= 1 and b <= 1 then
raster.set(xc, yc, color)
return
elseif a <= 1 then
for y = yc - b, yc + b do
raster.set(xc, y, color)
end
return
elseif b <= 1 then
for x = xc - a, xc + a do
raster.set(x, yc, color)
end
return
end
local x = 0
local y = b
local a2 = a * a
local b2 = b * b
local d1 = b2 - (a2 * b) + (0.25 * a2)
local dx = 2 * b2 * x
local dy = 2 * a2 * y
while dx < dy do
raster.set(xc + x, yc + y, color)
raster.set(xc - x, yc + y, color)
raster.set(xc + x, yc - y, color)
if d1 < 0 then
x = x + 1
dx = dx + (2 * b2)
d1 = d1 + dx + b2
else
x = x + 1
y = y - 1
dx = dx + (2 * b2)
dy = dy - (2 * a2)
d1 = d1 + dx - dy + b2
end
end
local d2 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2
while y >= 0 do
raster.set(xc + x, yc + y, color)
raster.set(xc - x, yc + y, color)
raster.set(xc + x, yc - y, color)
raster.set(xc - x, yc - y, color)
if d2 > 0 then
y = y - 1
dy = dy - (2 * a2)
d2 = d2 - dy + a2
else
y = y - 1
x = x + 1
dx = dx + (2 * b2)
dy = dy - (2 * a2)
d2 = d2 + dx - dy + a2
end
end
end
function raster.fillCircle(x, y, r, color)
x, y = math.floor(x + 0.5), math.floor(y + 0.5)
r = math.floor(r + 0.5)
if r <= 0 then return end
local minX, maxX = x - r, x + r
local minY, maxY = y - r, y + r
for py = minY, maxY do
for px = minX, maxX do
local dx, dy = px - x, py - y
local distSquared = dx*dx + dy*dy
if distSquared <= r*r then
raster.set(px, py, color)
end
end
end
end
function raster.fillEllipse(x1, y1, x2, y2, color)
local centerX = (x1 + x2) / 2
local centerY = (y1 + y2) / 2
local a = math.abs(x2 - x1) / 2
local b = math.abs(y2 - y1) / 2
centerX = math.floor(centerX + 0.5)
centerY = math.floor(centerY + 0.5)
a = math.floor(a + 0.5)
b = math.floor(b + 0.5)
if a <= 0 or b <= 0 then return end
if a == b then
raster.fillCircle(centerX, centerY, a, color)
return
end
local minX = centerX - a
local maxX = centerX + a
local minY = centerY - b
local maxY = centerY + b
for y = minY, maxY do
for x = minX, maxX do
local dx = x - centerX
local dy = y - centerY
local value = (dx*dx)/(a*a) + (dy*dy)/(b*b)
if value <= 1 then
raster.set(x, y, color)
end
end
end
end
return raster
-125
View File
@@ -1,125 +0,0 @@
local serialize = {}
function serialize.string(str)
return '"'..str:gsub("[%z\1-\31\34\92\127-\159]",function(c)
local byte = c:byte()
if byte== 7 then return "\\a" end
if byte== 8 then return "\\b" end
if byte== 9 then return "\\t" end
if byte==10 then return "\\n" end
if byte==11 then return "\\v" end
if byte==12 then return "\\f" end
if byte==13 then return "\\r" end
if byte==34 then return "\\\"" end
if byte==92 then return "\\\\" end
return string.format("\\x%02x",byte)
end)..'"'
end
function serialize.table(tbl,colors,stack)
stack = table.copy(stack or {})
table.insert(stack,tbl)
local keyAmount = 0
local keyNumber = true
local out = ""
local first = true
for key,val in pairs(tbl) do
if not first then out=out..",\n" end
first=false
out=out.." "
if type(key)=="string" then
if key:match("^[%a_][%w_]*$") then
out=out..key.."="
else
out=out..'['..serialize.string(key)..']='
end
else
out=out.."["..tostring(key).."]="
end
if type(key)~="number" then
keyNumber=false
end
local success,reason = pcall(function()
local valStr = ""
if type(val)=="table" then
if #stack>4 or table.find(stack,val) then
valStr="..."
else
valStr=serialize.table(val,colors,stack)
end
elseif type(val)=="string" then
local lines = {}
for line in (val.."\n"):gmatch("([^\n]*)\n") do table.insert(lines,line) end
if #lines[#lines]==0 then
lines[#lines]=nil
lines[#lines]=lines[#lines].."\n"
end
for i=1,#lines do
if i<#lines then
lines[i]=serialize.string(lines[i].."\n")
else
lines[i]=serialize.string(lines[i])
end
end
valStr=table.concat(lines," ..\n ")
else
valStr=tostring(val)
end
local lines = {}
for line in (valStr.."\n"):gmatch("([^\n]*)\n") do table.insert(lines,line) end
out=out..table.concat(lines,"\n ")
lines = nil
keyAmount=keyAmount+1
end)
if not success then
if colors then out=out.."\x1b[91m" end
out=out.."["..tostring(reason).."]"
if colors then out=out.."\x1b[39m" end
end
coroutine.yield()
end
local metatbl = getmetatable(tbl)
local metakeys = {}
if type(metatbl)=="table" then
for i,v in pairs(metatbl) do
keyNumber=false
table.insert(metakeys,i)
end
end
if #metakeys>0 then
out=out.."\n "
if colors then out=out.."\x1b[92m" end
if table.find(metakeys,"__tostring") then
out=out.."tostring: "..serialize.string(tostring(tbl)).."\n "
table.remove(metakeys,table.find(metakeys,"__tostring"))
end
out=out..table.concat(metakeys,", ")
if colors then out=out.."\x1b[39m" end
end
if keyAmount==0 then return "{}" end
if keyNumber then
-- fix strings not being serialised
local vals = {}
for _,v in pairs(tbl) do
if #vals>=5 and #stack>1 then
table.insert(vals,"...")
break
end
if type(v)=="table" then
table.insert(vals,serialize.table(v,colors,stack))
elseif type(v)=="string" then
table.insert(vals,serialize.string(v))
else
table.insert(vals,tostring(v))
end
end
return "{"..table.concat(vals,", ").."}"
end
return "{\n"..out.."\n}"
end
return serialize
-102
View File
@@ -1,102 +0,0 @@
local unicodeLib
local LLunicode
if table.copy then
unicodeLib = table.copy(unicode)
LLunicode = table.copy(unicode)
else
unicodeLib = {}
LLunicode = unicode
end
function unicodeLib.readCodePoint(readByte)
checkArg(1,readByte,"function")
local function inRange(min,max,...)
for _,v in ipairs({...}) do
if not (v and v>=min and v<max) then return false end
end
return true
end
local byte = readByte()
if byte==nil then return end
if byte < 0x80 then
-- ASCII character (0xxxxxxx)
return byte
elseif byte < 0xC0 then
-- Continuation byte (10xxxxxx), invalid at start position
return nil
elseif byte < 0xE0 then
-- 2-byte sequence (110xxxxx 10xxxxxx)
local byte2 = readByte()
if byte2==nil then return nil end
if inRange(0x80,0xC0,byte2) then
local code_point = ((byte & 0x1F) << 6) | (byte2 & 0x3F)
return code_point
end
elseif byte < 0xF0 then
-- 3-byte sequence (1110xxxx 10xxxxxx 10xxxxxx)
local byte2, byte3 = readByte(), readByte()
if byte2==nil and byte3==nil then return nil end
if inRange(0x80,0xC0,byte2,byte3)then
local code_point = ((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F)
return code_point
end
elseif byte < 0xF8 then
-- 4-byte sequence (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
local byte2, byte3, byte4 = readByte(), readByte(), readByte()
if byte2==nil and byte3==nil and byte4==nil then return nil end
if inRange(0x80,0xC0,byte2,byte3,byte4) then
local code_point = ((byte & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F)
return code_point
end
end
-- Invalid UTF-8 byte sequence
return nil
end
function unicodeLib.readChar(readByte)
checkArg(1,readByte,"function")
return LLunicode.char(unicodeLib.readCodePoint(readByte))
end
function unicodeLib.codepoint(chr)
checkArg(1,chr,"string")
local ptr = 1
return unicode.readCodePoint(function()
local byte = chr:byte(ptr)
ptr=ptr+1
return byte
end),ptr-1
end
function unicodeLib.iterate(readByte)
checkArg(1,readByte,"string","function")
if type(readByte)=="string" then
local str,ptr = readByte,0
readByte = function()
ptr=ptr+1
return str:byte(ptr)
end
end
return function()
local point = unicodeLib.readCodePoint(readByte)
if point==nil then return nil end
return LLunicode.char(point),point
end
end
unicodeLib.char = LLunicode.char
unicodeLib.charWidth = LLunicode.charWidth
unicodeLib.isWide = LLunicode.isWide
unicodeLib.len = LLunicode.len
unicodeLib.lower = LLunicode.lower
unicodeLib.reverse = LLunicode.reverse
unicodeLib.sub = LLunicode.sub
unicodeLib.upper = LLunicode.upper
unicodeLib.wlen = LLunicode.wlen
unicodeLib.wtrunc = LLunicode.wtrunc
return unicodeLib