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:
@@ -0,0 +1,97 @@
|
||||
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
|
||||
@@ -0,0 +1,14 @@
|
||||
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
|
||||
@@ -0,0 +1,55 @@
|
||||
local computer = require("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
|
||||
@@ -0,0 +1,506 @@
|
||||
local loadfile = ... -- raw loadfile from boot.lua
|
||||
local unicode, component, computer
|
||||
|
||||
if loadfile then
|
||||
unicode = loadfile("/lib/unicode.lua")(loadfile)
|
||||
component = loadfile("/lib/component.lua")(loadfile)
|
||||
computer = _G.computer
|
||||
elseif require then
|
||||
unicode = require("unicode")
|
||||
component = require("component")
|
||||
computer = require("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)
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 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
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
local fs = require("filesystem")
|
||||
local computer = require("computer")
|
||||
|
||||
local log = {}
|
||||
|
||||
function log.add(text, logType)
|
||||
checkArg(1, text, "string")
|
||||
checkArg(2, logType, "string", "nil")
|
||||
if logType ~= "debug" and logType ~= "info" and logType ~= "warning" and logType ~= "error" and logType then
|
||||
error("Log type must either be debug, info, warning or error.")
|
||||
end
|
||||
if not logType then
|
||||
logType = "debug"
|
||||
end
|
||||
local handle = fs.open("/halyde/system.log", "a")
|
||||
local time = computer.uptime()
|
||||
local logText = string.format("[%02d:%02d:%02d:%02d] " .. text, math.floor(time / 86400), math.floor(time / 3600 % 24), math.floor(time / 60 % 60), math.floor(time % 60)) .. "\n"
|
||||
if logType == "debug" then
|
||||
handle:write("\27[37m" .. logText)
|
||||
elseif logType == "info" then
|
||||
handle:write("\27[97m" .. logText)
|
||||
elseif logType == "warning" then
|
||||
handle:write("\27[93m" .. logText)
|
||||
else
|
||||
handle:write("\27[91m" .. logText)
|
||||
end
|
||||
handle:close()
|
||||
end
|
||||
|
||||
return log
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
local raster = {
|
||||
["units"]={},
|
||||
["defaultBackgroundColor"]=0x000000,
|
||||
["defaultForegroundColor"]=0xFFFFFF,
|
||||
["displayWidth"]=0,
|
||||
["displayHeight"]=0,
|
||||
["charWidth"]=0,
|
||||
["charHeight"]=0,
|
||||
["backgroundColor"]=0xFFFFFF
|
||||
}
|
||||
|
||||
local component = require("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
|
||||
@@ -0,0 +1,125 @@
|
||||
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
@@ -0,0 +1,102 @@
|
||||
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
|
||||
Reference in New Issue
Block a user