rewrite
This commit is contained in:
303
Build/boot/cct/boot.lua
Executable file
303
Build/boot/cct/boot.lua
Executable file
@@ -0,0 +1,303 @@
|
|||||||
|
local term = term
|
||||||
|
local os = os
|
||||||
|
-- Function to write text to the terminal with special character handling
|
||||||
|
local function write(text)
|
||||||
|
local x, y = term.getCursorPos()
|
||||||
|
local w, h = term.getSize()
|
||||||
|
|
||||||
|
for i = 1, #text do
|
||||||
|
local c = text:sub(i, i)
|
||||||
|
|
||||||
|
if c == "\n" then
|
||||||
|
y = y + 1
|
||||||
|
x = 1
|
||||||
|
elseif c == "\t" then
|
||||||
|
local tabSize = 4
|
||||||
|
local spaces = tabSize - ((x - 1) % tabSize)
|
||||||
|
term.write(string.rep(" ", spaces))
|
||||||
|
x = x + spaces
|
||||||
|
elseif c == "\b" then
|
||||||
|
if x > 1 then
|
||||||
|
x = x - 1
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
term.write(" ")
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if x <= w and y <= h then
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
term.write(c)
|
||||||
|
x = x + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle wrapping if we go past right edge
|
||||||
|
if x > w then
|
||||||
|
x = 1
|
||||||
|
y = y + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle scrolling if we go past bottom
|
||||||
|
if y-1 > h then
|
||||||
|
term.scroll(1)
|
||||||
|
y = h
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to display critical errors and halt the system
|
||||||
|
local function displaySuperBadError(err)
|
||||||
|
term.setBackgroundColor(0x1)
|
||||||
|
term.setTextColor(0x4)
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
term.write("A critical error occurred while loading the system:")
|
||||||
|
term.setCursorPos(1, 3)
|
||||||
|
write(err)
|
||||||
|
while true do end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Wrap all in xpcall to catch errors
|
||||||
|
local ok, err = xpcall(function()
|
||||||
|
local apis={}
|
||||||
|
|
||||||
|
-- List of standard Lua globals
|
||||||
|
local lua = {
|
||||||
|
coroutine = true,
|
||||||
|
debug = true,
|
||||||
|
_HOST = true,
|
||||||
|
_VERSION = true,
|
||||||
|
assert = true,
|
||||||
|
collectgarbage = true,
|
||||||
|
error = true,
|
||||||
|
gcinfo = true,
|
||||||
|
getfenv = true,
|
||||||
|
getmetatable = true,
|
||||||
|
ipairs = true,
|
||||||
|
__inext = true,
|
||||||
|
load = true,
|
||||||
|
math = true,
|
||||||
|
next = true,
|
||||||
|
pairs = true,
|
||||||
|
pcall = true,
|
||||||
|
rawequal = true,
|
||||||
|
rawget = true,
|
||||||
|
rawlen = true,
|
||||||
|
rawset = true,
|
||||||
|
select = true,
|
||||||
|
setfenv = true,
|
||||||
|
setmetatable = true,
|
||||||
|
string = true,
|
||||||
|
table = true,
|
||||||
|
tonumber = true,
|
||||||
|
tostring = true,
|
||||||
|
type = true,
|
||||||
|
xpcall = true,
|
||||||
|
_G=true
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Move all non-Lua standard library globals into the apis table
|
||||||
|
for i,v in pairs(_G) do
|
||||||
|
if not lua[i] or lua[i]==nil then
|
||||||
|
apis[i]=v
|
||||||
|
_G[i]=nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set up terminal default colors
|
||||||
|
apis.term.setPaletteColor(0x1, 0x000000) -- #000000
|
||||||
|
apis.term.setPaletteColor(0x2, 0xFFFFFF) -- #FFFFFF
|
||||||
|
apis.term.setPaletteColor(0x4, 0xFF0000) -- #FF0000
|
||||||
|
apis.term.setPaletteColor(0x8, 0x00FF00) -- #00FF00
|
||||||
|
apis.term.setPaletteColor(0x10, 0x0000FF) -- #0000FF
|
||||||
|
apis.term.setPaletteColor(0x20, 0x00FFFF) -- #00FFFF
|
||||||
|
apis.term.setPaletteColor(0x40, 0xFF00FF) -- #FF00FF
|
||||||
|
apis.term.setPaletteColor(0x80, 0xFFFF00) -- #FFFF00
|
||||||
|
apis.term.setPaletteColor(0x100, 0xFF6D00) -- #FF6D00
|
||||||
|
apis.term.setPaletteColor(0x200, 0x6DFF55) -- #6DFF55
|
||||||
|
apis.term.setPaletteColor(0x400, 0x24FFFF) -- #24FFFF
|
||||||
|
apis.term.setPaletteColor(0x800, 0x924900) -- #924900
|
||||||
|
apis.term.setPaletteColor(0x1000, 0x6D6D55) -- #6D6D55
|
||||||
|
apis.term.setPaletteColor(0x2000, 0xDBDBAA) -- #DBDBAA
|
||||||
|
apis.term.setPaletteColor(0x4000, 0x6D00FF) -- #6D00FF
|
||||||
|
apis.term.setPaletteColor(0x8000, 0xB6FF00) -- #B6FF00
|
||||||
|
|
||||||
|
-- Wrapper to read files
|
||||||
|
local function getFile(path)
|
||||||
|
local file = apis.fs.open(path, "r")
|
||||||
|
if not file then displaySuperBadError("Could not open file: "..path) end
|
||||||
|
local content = file.readAll()
|
||||||
|
file.close()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Load kernel first if it fails, we can't continue so we display an error
|
||||||
|
local Kernel = load(getFile("/$/boot/kernel.lua"),"@Kernel")
|
||||||
|
local initFs = load(getFile("/$/boot/cct/initdisks","@Init_disks"))(apis)
|
||||||
|
local fs = load(getFile("/$/boot/initfs"),"@InitFs")()
|
||||||
|
if not Kernel then
|
||||||
|
displaySuperBadError("Could not load kernel.")
|
||||||
|
end
|
||||||
|
if not initFs then
|
||||||
|
displaySuperBadError("Could not load initdisks.")
|
||||||
|
end
|
||||||
|
if not fs then
|
||||||
|
displaySuperBadError("Could not load initfs.")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set up event queue
|
||||||
|
local eventQueue = {}
|
||||||
|
local lkeys={}
|
||||||
|
lkeys[apis.keys.enter]="\n"
|
||||||
|
lkeys[apis.keys.backspace]="\b"
|
||||||
|
lkeys[apis.keys.tab]="\t"
|
||||||
|
|
||||||
|
-- Function to queue events
|
||||||
|
local function queueEvent(event, ...)
|
||||||
|
table.insert(eventQueue, {event, ...})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set up computer api
|
||||||
|
local computer = {
|
||||||
|
time = function() return apis.os.epoch("utc") end,
|
||||||
|
clock = apis.os.clock,
|
||||||
|
shutdown = apis.os.shutdown,
|
||||||
|
reboot = apis.os.reboot,
|
||||||
|
getMachineEvent = function()
|
||||||
|
if #eventQueue > 0 then
|
||||||
|
return table.unpack(table.remove(eventQueue, 1))
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
getEEPROM = function()
|
||||||
|
return getFile("/startup.lua")
|
||||||
|
end,
|
||||||
|
setEEPROM = function(_,text)
|
||||||
|
local h = apis.fs.open("/startup.lua", "w")
|
||||||
|
h.write(text)
|
||||||
|
h.close()
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Set up terminal colors
|
||||||
|
local icolors={
|
||||||
|
[0x1] =0, -- #000000
|
||||||
|
[0x2] =1, -- #FFFFFF
|
||||||
|
[0x4] =2, -- #FF0000
|
||||||
|
[0x8] =3, -- #00FF00
|
||||||
|
[0x10] =4, -- #0000FF
|
||||||
|
[0x20] =5, -- #00FFFF
|
||||||
|
[0x40] =6, -- #FF00FF
|
||||||
|
[0x80] =7, -- #FFFF00
|
||||||
|
[0x100] =8, -- #FF6D00
|
||||||
|
[0x200] =9, -- #6DFF55
|
||||||
|
[0x400] =10, -- #24FFFF
|
||||||
|
[0x800] =11, -- #924900
|
||||||
|
[0x1000] =12, -- #6D6D55
|
||||||
|
[0x2000] =13, -- #DBDBAA
|
||||||
|
[0x4000] =14, -- #6D00FF
|
||||||
|
[0x8000] =15 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
local colors={
|
||||||
|
[0]=0x0001, -- #000000
|
||||||
|
0x0002, -- #FFFFFF
|
||||||
|
0x0004, -- #FF0000
|
||||||
|
0x0008, -- #00FF00
|
||||||
|
0x0010, -- #0000FF
|
||||||
|
0x0020, -- #00FFFF
|
||||||
|
0x0040, -- #FF00FF
|
||||||
|
0x0080, -- #FFFF00
|
||||||
|
0x0100, -- #FF6D00
|
||||||
|
0x0200, -- #6DFF55
|
||||||
|
0x0400, -- #24FFFF
|
||||||
|
0x0800, -- #924900
|
||||||
|
0x1000, -- #6D6D55
|
||||||
|
0x2000, -- #DBDBAA
|
||||||
|
0x4000, -- #6D00FF
|
||||||
|
0x8000 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
apis.term.setBackgroundColor(0x1)
|
||||||
|
apis.term.setTextColor(0x1000)
|
||||||
|
apis.term.clear()
|
||||||
|
apis.term.setCursorPos(1, 1)
|
||||||
|
|
||||||
|
-- Make the kernel coroutine so we can hook its execution
|
||||||
|
local kernelCoro = coroutine.create(function()
|
||||||
|
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init", {
|
||||||
|
print=function(_,text) write(text.."\n") end,
|
||||||
|
printInline=function(_,text) write(text) end,
|
||||||
|
clear=function() apis.term.clear() apis.term.setCursorPos(1,1) end,
|
||||||
|
setCursorPos=function(_,x,y) apis.term.setCursorPos(x,y) end,
|
||||||
|
getCursorPos=function() return apis.term.getCursorPos() end,
|
||||||
|
getSize=function() return apis.term.getSize() end,
|
||||||
|
setBackgroundColor=function(_,color) apis.term.setBackgroundColor(colors[color]) end,
|
||||||
|
setTextColor=function(_,color) apis.term.setTextColor(colors[color]) end,
|
||||||
|
getBackgroundColor=function() return icolors[apis.term.getBackgroundColor()] end,
|
||||||
|
getTextColor=function() return icolors[apis.term.getTextColor()] end
|
||||||
|
}, computer, fs, "$")
|
||||||
|
if not ok then
|
||||||
|
displaySuperBadError(err)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Diffine a coroutine.resumeWithTimeout function to avoid hanging the system, time is in milliseconds
|
||||||
|
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||||
|
local startTime = computer.time()
|
||||||
|
debug.sethook(co, function()
|
||||||
|
if computer.time() > startTime + timeout then
|
||||||
|
return coroutine.yield("timeout")
|
||||||
|
end
|
||||||
|
end, "", 1000)
|
||||||
|
local ret = {coroutine.resume(co, ...)}
|
||||||
|
if ret[1]=="timeout" then
|
||||||
|
return nil, "Coroutine timed out"
|
||||||
|
elseif ret[1]==false then
|
||||||
|
return false, ret[2]
|
||||||
|
else
|
||||||
|
debug.sethook(co)
|
||||||
|
return true, table.unpack(ret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
write("Loaded in "..tostring(apis.os.clock()).." seconds.\n")
|
||||||
|
|
||||||
|
-- Main loop to run the kernel coroutine
|
||||||
|
while true do
|
||||||
|
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
||||||
|
apis.os.queueEvent("NoSleep")
|
||||||
|
local exit = false
|
||||||
|
while not exit do
|
||||||
|
local event = {coroutine.yield()}
|
||||||
|
if event[1] == "char" then
|
||||||
|
queueEvent("keyTyped", 1, event[2])
|
||||||
|
elseif event[1] == "key" then
|
||||||
|
queueEvent("keyPressed", 1, event[2])
|
||||||
|
if lkeys[event[2]] then
|
||||||
|
queueEvent("keyTyped", 1, lkeys[event[2]])
|
||||||
|
end
|
||||||
|
elseif event[1] == "key_up" then
|
||||||
|
queueEvent("keyReleased", 1, event[2])
|
||||||
|
elseif event[1] == "disk" then
|
||||||
|
queueEvent("componentAdded", "disk")
|
||||||
|
elseif event[1] == "disk_eject" then
|
||||||
|
queueEvent("componentRemoved", "disk")
|
||||||
|
elseif event[1] == "NoSleep" then
|
||||||
|
exit=true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if status == false or coroutine.status(kernelCoro)=="dead" then
|
||||||
|
displaySuperBadError("Kernel error: "..tostring(err))
|
||||||
|
coroutine.yield("key")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end, debug.traceback)
|
||||||
|
if not ok then
|
||||||
|
displaySuperBadError("Fatal error during boot: "..err)
|
||||||
|
end
|
||||||
|
while true do coroutine.yield() end
|
||||||
119
Build/boot/cct/eeprom
Executable file
119
Build/boot/cct/eeprom
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
-- UnBIOS by JackMacWindows
|
||||||
|
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
||||||
|
-- To use, just place a `bios.lua` in the root of the drive, and run this program
|
||||||
|
-- Here's a list of things that are irreversibly changed:
|
||||||
|
-- * both `bit` and `bit32` are kept for compatibility
|
||||||
|
-- * string metatable blocking (on old versions of CC)
|
||||||
|
-- In addition, if `debug` is not available these things are also irreversibly changed:
|
||||||
|
-- * old Lua 5.1 `load` function (for loading from a function)
|
||||||
|
-- * `loadstring` prefixing (before CC:T 1.96.0)
|
||||||
|
-- * `http.request`
|
||||||
|
-- * `os.shutdown` and `os.reboot`
|
||||||
|
-- * `peripheral`
|
||||||
|
-- * `turtle.equip[Left|Right]`
|
||||||
|
-- Licensed under the MIT license
|
||||||
|
local args = {...}
|
||||||
|
if _HOST:find("UnBIOS") then return end
|
||||||
|
local keptAPIs = {keys=true, bit32 = true, bit = true, ccemux = true, config = true, coroutine = true, debug = true, fs = true, http = true, mounter = true, os = true, periphemu = true, peripheral = true, redstone = true, rs = true, term = true, utf8 = true, _HOST = true, _CC_DEFAULT_SETTINGS = true, _CC_DISABLE_LUA51_FEATURES = true, _VERSION = true, assert = true, collectgarbage = true, error = true, gcinfo = true, getfenv = true, getmetatable = true, ipairs = true, __inext = true,load = true, loadstring = true, math = true, newproxy = true, next = true, pairs = true, pcall = true, rawequal = true, rawget = true, rawlen = true, rawset = true, select = true, setfenv = true, setmetatable = true, string = true, table = true, tonumber = true, tostring = true, type = true, unpack = true, xpcall = true, turtle = true, pocket = true, commands = true, _G = true}
|
||||||
|
local t = {}
|
||||||
|
for k in pairs(_G) do if not keptAPIs[k] then table.insert(t, k) end end
|
||||||
|
for _,k in ipairs(t) do _G[k] = nil end
|
||||||
|
local native = _G.term.native()
|
||||||
|
for _, method in ipairs {"nativePaletteColor", "nativePaletteColour", "screenshot"} do native[method] = _G.term[method] end
|
||||||
|
_G.term = native
|
||||||
|
if _G.http then
|
||||||
|
_G.http.checkURL = _G.http.checkURLAsync
|
||||||
|
_G.http.websocket = _G.http.websocketAsync
|
||||||
|
end
|
||||||
|
if _G.commands then _G.commands = _G.commands.native end
|
||||||
|
if _G.turtle then _G.turtle.native, _G.turtle.craft = nil end
|
||||||
|
local delete = {os = {"version", "pullEventRaw", "pullEvent", "run", "loadAPI", "unloadAPI", "sleep"}, http = _G.http and {"get", "post", "put", "delete", "patch", "options", "head", "trace", "listen", "checkURLAsync", "websocketAsync"}, fs = {"complete", "isDriveRoot"}}
|
||||||
|
for k,v in pairs(delete) do for _,a in ipairs(v) do _G[k][a] = nil end end
|
||||||
|
_G._HOST = _G._HOST .. " (UnBIOS)"
|
||||||
|
-- Set up TLCO
|
||||||
|
-- This functions by crashing `rednet.run` by removing `os.pullEventRaw`. Normally
|
||||||
|
-- this would cause `parallel` to throw an error, but we replace `error` with an
|
||||||
|
-- empty placeholder to let it continue and return without throwing. This results
|
||||||
|
-- in the `pcall` returning successfully, preventing the error-displaying code
|
||||||
|
-- from running - essentially making it so that `os.shutdown` is called immediately
|
||||||
|
-- after the new BIOS exits.
|
||||||
|
--
|
||||||
|
-- From there, the setup code is placed in `term.native` since it's the first
|
||||||
|
-- thing called after `parallel` exits. This loads the new BIOS and prepares it
|
||||||
|
-- for execution. Finally, it overwrites `os.shutdown` with the new function to
|
||||||
|
-- allow it to be the last function called in the original BIOS, and returns.
|
||||||
|
-- From there execution continues, calling the `term.redirect` dummy, skipping
|
||||||
|
-- over the error-handling code (since `pcall` returned ok), and calling
|
||||||
|
-- `os.shutdown()`. The real `os.shutdown` is re-added, and the new BIOS is tail
|
||||||
|
-- called, which effectively makes it run as the main chunk.
|
||||||
|
local olderror = error
|
||||||
|
_G.error = function() end
|
||||||
|
_G.term.redirect = function() end
|
||||||
|
function _G.term.native()
|
||||||
|
_G.term.native = nil
|
||||||
|
_G.term.redirect = nil
|
||||||
|
_G.error = olderror
|
||||||
|
term.setBackgroundColor(32768)
|
||||||
|
term.setTextColor(1)
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
term.setCursorBlink(true)
|
||||||
|
term.clear()
|
||||||
|
local file = fs.open("/$/boot/cct/boot.lua", "r")
|
||||||
|
if file == nil then
|
||||||
|
term.setCursorBlink(false)
|
||||||
|
term.setTextColor(16384)
|
||||||
|
term.write("Could not find /boot/cct/boot.lua. UnBIOS cannot continue.")
|
||||||
|
term.setCursorPos(1, 2)
|
||||||
|
term.write("Press any key to continue")
|
||||||
|
coroutine.yield("key")
|
||||||
|
os.shutdown()
|
||||||
|
end
|
||||||
|
local fn, err = loadstring(file.readAll(), "@bootloader")
|
||||||
|
file.close()
|
||||||
|
if fn == nil then
|
||||||
|
term.setCursorBlink(false)
|
||||||
|
term.setTextColor(16384)
|
||||||
|
term.write("Could not load /boot/cc/boot.lua. UnBIOS cannot continue.")
|
||||||
|
term.setCursorPos(1, 2)
|
||||||
|
term.write(err)
|
||||||
|
term.setCursorPos(1, 3)
|
||||||
|
term.write("Press any key to continue")
|
||||||
|
coroutine.yield("key")
|
||||||
|
os.shutdown()
|
||||||
|
end
|
||||||
|
setfenv(fn, _G)
|
||||||
|
local oldshutdown = os.shutdown
|
||||||
|
os.shutdown = function()
|
||||||
|
os.shutdown = oldshutdown
|
||||||
|
return fn(table.unpack(args))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if debug then
|
||||||
|
-- Restore functions that were overwritten in the BIOS
|
||||||
|
-- Apparently this has to be done *after* redefining term.native
|
||||||
|
local function restoreValue(tab, idx, name, hint)
|
||||||
|
local i, key, value = 1, debug.getupvalue(tab[idx], hint)
|
||||||
|
while key ~= name and key ~= nil do
|
||||||
|
key, value = debug.getupvalue(tab[idx], i)
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
tab[idx] = value or tab[idx]
|
||||||
|
end
|
||||||
|
restoreValue(_G, "loadstring", "nativeloadstring", 1)
|
||||||
|
restoreValue(_G, "load", "nativeload", 5)
|
||||||
|
if http then restoreValue(http, "request", "nativeHTTPRequest", 3) end
|
||||||
|
restoreValue(os, "shutdown", "nativeShutdown", 1)
|
||||||
|
restoreValue(os, "reboot", "nativeReboot", 1)
|
||||||
|
if turtle then
|
||||||
|
restoreValue(turtle, "equipLeft", "v", 1)
|
||||||
|
restoreValue(turtle, "equipRight", "v", 1)
|
||||||
|
end
|
||||||
|
do
|
||||||
|
local i, key, value = 1, debug.getupvalue(peripheral.isPresent, 2)
|
||||||
|
while key ~= "native" and key ~= nil do
|
||||||
|
key, value = debug.getupvalue(peripheral.isPresent, i)
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
_G.peripheral = value or peripheral
|
||||||
|
end
|
||||||
|
end
|
||||||
201
Build/boot/cct/initdisks
Executable file
201
Build/boot/cct/initdisks
Executable file
@@ -0,0 +1,201 @@
|
|||||||
|
local apis = ({...})[1]
|
||||||
|
local fs = apis.fs
|
||||||
|
local native = apis.peripheral
|
||||||
|
local peripheral = {}
|
||||||
|
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||||
|
|
||||||
|
function peripheral.getType(name)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return native.getType(name)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return native.call(side, "getTypeRemote", name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getNames()
|
||||||
|
local names = {}
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.isPresent(side) then
|
||||||
|
table.insert(names, side)
|
||||||
|
end
|
||||||
|
if native.hasType(side, "peripheral_hub") then
|
||||||
|
local hubSides = native.call(side, "getConnectedSides")
|
||||||
|
for _, hubSide in ipairs(hubSides) do
|
||||||
|
table.insert(names, hubSide)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return names
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- STORAGE
|
||||||
|
---------------------------------------------------------
|
||||||
|
local disks = {} -- real disks
|
||||||
|
local internal = {} -- "$" disk
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- HELPERS
|
||||||
|
---------------------------------------------------------
|
||||||
|
local function norm(path)
|
||||||
|
if not path or path == "" then return "/" end
|
||||||
|
return fs.combine("/", path)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Creates a disk object given a base path
|
||||||
|
local function createDisk(id, basePath, readonly, periph)
|
||||||
|
basePath = norm(basePath)
|
||||||
|
|
||||||
|
local disk = {address=id,isReadOnly=function() return readonly end}
|
||||||
|
|
||||||
|
function disk:spaceUsed()
|
||||||
|
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:spaceTotal()
|
||||||
|
return fs.getCapacity(basePath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:readAllText(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
if not fs.exists(p) then return nil, "file not found" end
|
||||||
|
local h = fs.open(p, "r")
|
||||||
|
local t = h.readAll()
|
||||||
|
h.close()
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:writeAllText(path, text)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
local h = fs.open(p, "w")
|
||||||
|
if not h then return nil, "cannot write" end
|
||||||
|
h.write(text)
|
||||||
|
h.close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:appendAllText(path, text)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
local h = fs.open(p, "a")
|
||||||
|
if not h then return nil, "cannot append" end
|
||||||
|
h.write(text)
|
||||||
|
h.close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:list(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
if not fs.exists(p) or not fs.isDir(p) then return nil, "not directory" end
|
||||||
|
return fs.list(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:fileExists(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.exists(p) and not fs.isDir(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:directoryExists(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.exists(p) and fs.isDir(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:makeDirectory(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
fs.makeDir(p)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:remove(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
if fs.exists(p) then fs.delete(p) end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:setLabel(label)
|
||||||
|
periph.setLabel(label)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:getLabel(label)
|
||||||
|
return periph.getLabel()
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:size(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.getSize(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
return disk
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- INTERNAL DISK "$" (mapped to "/")
|
||||||
|
---------------------------------------------------------
|
||||||
|
internal["$"] = createDisk("$", "/$", false, {
|
||||||
|
setLabel=function(label)
|
||||||
|
local h = fs.open("/.label", "w")
|
||||||
|
h.write(label)
|
||||||
|
h.close()
|
||||||
|
end,
|
||||||
|
getLabel=function()
|
||||||
|
local h = fs.open("/.label", "r")
|
||||||
|
local label = h.readAll()
|
||||||
|
h.close()
|
||||||
|
return label
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- SCAN REAL DISK PERIPHERALS
|
||||||
|
---------------------------------------------------------
|
||||||
|
local function refresh()
|
||||||
|
-- remove disks that no longer exist
|
||||||
|
for id, _ in pairs(disks) do
|
||||||
|
if not peripheral.getType(id) then
|
||||||
|
disks[id] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- detect new disks
|
||||||
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
|
if peripheral.getType(name) == "disk" then
|
||||||
|
if not disks[name] then
|
||||||
|
local mount = disk.getMountPath(name)
|
||||||
|
if mount then
|
||||||
|
disks[name] = createDisk(name, mount, false, disk)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- ITERATOR
|
||||||
|
---------------------------------------------------------
|
||||||
|
local function iter()
|
||||||
|
refresh()
|
||||||
|
-- first internal
|
||||||
|
local combined = {}
|
||||||
|
|
||||||
|
for id, obj in pairs(internal) do
|
||||||
|
combined[id] = obj
|
||||||
|
end
|
||||||
|
for id, obj in pairs(disks) do
|
||||||
|
combined[id] = obj
|
||||||
|
end
|
||||||
|
|
||||||
|
return pairs(combined)
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- MODULE RETURN
|
||||||
|
---------------------------------------------------------
|
||||||
|
return {
|
||||||
|
refresh = refresh,
|
||||||
|
list = iter
|
||||||
|
}
|
||||||
69
Build/boot/initfs
Executable file
69
Build/boot/initfs
Executable file
@@ -0,0 +1,69 @@
|
|||||||
|
local fs = {}
|
||||||
|
local disks = {}
|
||||||
|
local mounts = {}
|
||||||
|
|
||||||
|
local function resolve(path)
|
||||||
|
local mountPoint = "/"
|
||||||
|
for mount, disk in pairs(mounts) do
|
||||||
|
if path:sub(1, #mount) == mount then
|
||||||
|
if not mountPoint or #mount > #mountPoint then
|
||||||
|
mountPoint = mount
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local newPath = path:sub(#mountPoint + 1)
|
||||||
|
return disks[mounts[mountPoint]], newPath
|
||||||
|
end
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
function fs.update(initdisks)
|
||||||
|
disks = {}
|
||||||
|
for k, v in initdisks.list() do
|
||||||
|
disks[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.exists(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:directoryExists(newPath) or disk:fileExists(newPath)
|
||||||
|
end
|
||||||
|
function fs.isFile(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:fileExists(newPath)
|
||||||
|
end
|
||||||
|
function fs.isDir(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:directoryExists(newPath)
|
||||||
|
end
|
||||||
|
function fs.list(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:list(newPath)
|
||||||
|
end
|
||||||
|
function fs.makeDir(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:makeDirectory(newPath)
|
||||||
|
end
|
||||||
|
function fs.remove(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:remove(newPath)
|
||||||
|
end
|
||||||
|
function fs.readAllText(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:readAllText(newPath)
|
||||||
|
end
|
||||||
|
function fs.writeAllText(path, text)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:writeAllText(newPath, text)
|
||||||
|
end
|
||||||
|
function fs.appendAllText(path, text)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:appendAllText(newPath, text)
|
||||||
|
end
|
||||||
|
function fs.load(path)
|
||||||
|
return load(fs.readAllText(path), path)
|
||||||
|
end
|
||||||
|
function fs.mount(disk, mountPoint)
|
||||||
|
if not disks[disk] then return end
|
||||||
|
mounts[mountPoint] = disk
|
||||||
|
end
|
||||||
|
return fs
|
||||||
166
Build/boot/kernel.lua
Executable file
166
Build/boot/kernel.lua
Executable file
@@ -0,0 +1,166 @@
|
|||||||
|
local args = {...}
|
||||||
|
local apis = args[1]
|
||||||
|
local disks = args[2]
|
||||||
|
local arch = args[3]
|
||||||
|
local initPath = args[4]
|
||||||
|
local screen = args[5]
|
||||||
|
local computer = args[6]
|
||||||
|
local ifs = args[7]
|
||||||
|
local LOG_Text = ""
|
||||||
|
local kernel = {}
|
||||||
|
kernel.process = "Kernel"
|
||||||
|
kernel.user = "root"
|
||||||
|
kernel.group = "root"
|
||||||
|
kernel.groups = {0}
|
||||||
|
kernel.uid = 0
|
||||||
|
kernel.gid = 0
|
||||||
|
kernel.stage = "start"
|
||||||
|
kernel.key = {}
|
||||||
|
kernel.chache = {}
|
||||||
|
kernel.chache.preload = {}
|
||||||
|
local windowsExp = false
|
||||||
|
|
||||||
|
function kernel.log(msg, level)
|
||||||
|
LOG_Text = LOG_Text..tostring(computer.time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
||||||
|
if kernel.stage == "start" then
|
||||||
|
screen:print(tostring(computer.time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function kernel.PANIC(msg)
|
||||||
|
kernel.log("PANIC: "..msg, "PANIC")
|
||||||
|
pcall(kernel["saveLog"])
|
||||||
|
screen:setTextColor(2)
|
||||||
|
screen:setBackgroundColor(0)
|
||||||
|
screen:clear()
|
||||||
|
screen:setCursorPos(1,1)
|
||||||
|
screen:print(LOG_Text)
|
||||||
|
screen:print("KERNEL PANIC!\n"..msg.."\nSystem halted.")
|
||||||
|
screen:print("Press any key to continue...")
|
||||||
|
while true do
|
||||||
|
local event={computer:getMachineEvent()}
|
||||||
|
if event[1]=="keyPressed" then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
computer:reboot()
|
||||||
|
end
|
||||||
|
kernel.panic=kernel.PANIC
|
||||||
|
|
||||||
|
if windowsExp then
|
||||||
|
screen:setTextColor(1)
|
||||||
|
screen:setBackgroundColor(4)
|
||||||
|
screen:clear()
|
||||||
|
local w,h = screen:getSize()
|
||||||
|
screen:setCursorPos(3,5)
|
||||||
|
screen:print(":(")
|
||||||
|
screen:setCursorPos(3,7)
|
||||||
|
screen:print("Your PC ran into a problem and needs to restart. We're just collecting some error")
|
||||||
|
screen:setCursorPos(3,8)
|
||||||
|
screen:print("info, and then we'll restart for you.\n")
|
||||||
|
screen:setCursorPos(3,h-5)
|
||||||
|
screen:print("Stop code: average windows experience")
|
||||||
|
screen:setCursorPos(1,h)
|
||||||
|
screen:print("Press any key to continue... jk reboot it yourself lazy")
|
||||||
|
while true do end
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.log("Kernel loaded.", "INFO")
|
||||||
|
kernel.log("Mounting disks...", "INFO")
|
||||||
|
disks.refresh()
|
||||||
|
ifs.update(disks)
|
||||||
|
ifs.mount("$", "/")
|
||||||
|
|
||||||
|
function kernel.saveLog()
|
||||||
|
ifs.writeAllText("/var/log/syslog.log", LOG_Text)
|
||||||
|
end
|
||||||
|
|
||||||
|
local fstab=ifs.readAllText("/etc/fstab")
|
||||||
|
local split = function(str, delim, maxResultCountOrNil)
|
||||||
|
assert(#delim == 1, "only delim len 1 supported for now")
|
||||||
|
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
|
||||||
|
local rv = {}
|
||||||
|
local buf = ""
|
||||||
|
for i = 1, #str do
|
||||||
|
local c = string.sub(str,i,i)
|
||||||
|
if #rv ~= maxResultCountOrNil and c == delim then
|
||||||
|
table.insert(rv, buf)
|
||||||
|
buf = ""
|
||||||
|
else
|
||||||
|
buf = buf..c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(rv, buf)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(split(fstab,"\n")) do
|
||||||
|
if v:sub(1,1)=="U" then
|
||||||
|
local id=""
|
||||||
|
for i=3,#v do
|
||||||
|
if v:sub(i,i)==";" then
|
||||||
|
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
|
||||||
|
id=v:sub(3,i-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local path=v:sub(#id+4)
|
||||||
|
kernel.log("Mounted "..id.." to "..path)
|
||||||
|
ifs.mount(id,path)
|
||||||
|
::endline::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ifs.remove("/tmp")
|
||||||
|
ifs.makeDir("/tmp")
|
||||||
|
|
||||||
|
local drivers={}
|
||||||
|
drivers.raw={}
|
||||||
|
drivers.processes={}
|
||||||
|
drivers.prior={}
|
||||||
|
drivers.type={}
|
||||||
|
for i=0, 99 do
|
||||||
|
drivers.prior[i]={}
|
||||||
|
end
|
||||||
|
|
||||||
|
function drivers.register(prior,object)
|
||||||
|
drivers.raw[#drivers.raw+1]=object
|
||||||
|
if object.main then drivers.processes[#drivers.processes+1]=object.main end
|
||||||
|
if drivers.prior[prior]==nil then drivers.prior[prior]={} end
|
||||||
|
drivers.prior[prior][#drivers.prior[prior]+1]=object
|
||||||
|
if drivers.type[object.type]==nil then drivers.type[object.type]={} end
|
||||||
|
drivers.type[object.type][#drivers.type[object.type]+1]=object
|
||||||
|
end
|
||||||
|
|
||||||
|
local modules={}
|
||||||
|
for i=0, 99 do
|
||||||
|
modules[i]={}
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(ifs.list("/lib/modules/Hyperion/")) do
|
||||||
|
local prior=tonumber(v:sub(1,2))
|
||||||
|
modules[prior][#modules[prior]+1]="/lib/modules/Hyperion/"..v
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.drivers=drivers
|
||||||
|
kernel.ifs=ifs
|
||||||
|
kernel.apis=apis
|
||||||
|
kernel.computer=computer
|
||||||
|
kernel.initPath=initPath
|
||||||
|
kernel.arch=arch
|
||||||
|
kernel.initdisks=initdisks
|
||||||
|
kernel.screen=screen
|
||||||
|
for _,p in ipairs(modules) do
|
||||||
|
for _,v in ipairs(p) do
|
||||||
|
local code=ifs.readAllText(v)
|
||||||
|
local func,err=load(code,"@"..v)
|
||||||
|
if not func then kernel.log("ModuLoadErr: "..tostring(err)) goto skip end
|
||||||
|
local status, err = xpcall(func,debug.traceback,kernel)
|
||||||
|
if not status then goto skip end
|
||||||
|
if not err then goto skip end
|
||||||
|
if not err.init then goto skip end
|
||||||
|
local ok, err = xpcall(status.main,debug.traceback)
|
||||||
|
if not ok then kernel.log("ModuInitErr: "..tostring(err)) end
|
||||||
|
::skip::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
kernel.PANIC("Execution complete")
|
||||||
40
Build/boot/oc/boot.lua
Executable file
40
Build/boot/oc/boot.lua
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
local lua = {
|
||||||
|
coroutine = true,
|
||||||
|
debug = true,
|
||||||
|
_HOST = true,
|
||||||
|
_VERSION = true,
|
||||||
|
assert = true,
|
||||||
|
collectgarbage = true,
|
||||||
|
error = true,
|
||||||
|
gcinfo = true,
|
||||||
|
getfenv = true,
|
||||||
|
getmetatable = true,
|
||||||
|
ipairs = true,
|
||||||
|
__inext = true,
|
||||||
|
load = true,
|
||||||
|
math = true,
|
||||||
|
next = true,
|
||||||
|
pairs = true,
|
||||||
|
pcall = true,
|
||||||
|
rawequal = true,
|
||||||
|
rawget = true,
|
||||||
|
rawlen = true,
|
||||||
|
rawset = true,
|
||||||
|
select = true,
|
||||||
|
setfenv = true,
|
||||||
|
setmetatable = true,
|
||||||
|
string = true,
|
||||||
|
table = true,
|
||||||
|
tonumber = true,
|
||||||
|
tostring = true,
|
||||||
|
type = true,
|
||||||
|
xpcall = true,
|
||||||
|
_G=true
|
||||||
|
}
|
||||||
|
|
||||||
|
for i,v in pairs(_G) do
|
||||||
|
if not lua[i] or lua[i]==nil then
|
||||||
|
apis[i]=v
|
||||||
|
_G[i]=nil
|
||||||
|
end
|
||||||
|
end
|
||||||
18
Build/boot/oc/eeprom
Executable file
18
Build/boot/oc/eeprom
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
checkArg=nil
|
||||||
|
local oldcomputer=computer
|
||||||
|
_G.computer=nil
|
||||||
|
local os=os
|
||||||
|
_G.os=nil
|
||||||
|
|
||||||
|
function component.wrap(address)
|
||||||
|
local methods=oldcomponent.methods(address)
|
||||||
|
local object={}
|
||||||
|
for _,method in ipairs(methods) do
|
||||||
|
object[method]=function(_,...)
|
||||||
|
return oldcomponent.invoke(address,method,...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
local
|
||||||
1
Build/boot/oc/initfs.lua
Executable file
1
Build/boot/oc/initfs.lua
Executable file
@@ -0,0 +1 @@
|
|||||||
|
local fs={}
|
||||||
2
Build/etc/fstab
Executable file
2
Build/etc/fstab
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
U $;/
|
||||||
|
U devfs0000;/dev/
|
||||||
3543
Build/lib/LibDeflate
Executable file
3543
Build/lib/LibDeflate
Executable file
File diff suppressed because it is too large
Load Diff
9
Build/lib/deflate
Executable file
9
Build/lib/deflate
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
local deflate=require("LibDeflate")
|
||||||
|
local lib={}
|
||||||
|
lib.compress=function(data)
|
||||||
|
return deflate:CompressDeflate(data)
|
||||||
|
end
|
||||||
|
lib.decompress=function(data)
|
||||||
|
return deflate:DecompressDeflate(data)
|
||||||
|
end
|
||||||
|
return lib
|
||||||
0
docs/api/kernel.txt → Build/lib/globals/string
Normal file → Executable file
0
docs/api/kernel.txt → Build/lib/globals/string
Normal file → Executable file
0
Build/lib/globals/table
Executable file
0
Build/lib/globals/table
Executable file
137
Build/lib/modules/Hyperion/00_stdlib.kmod
Executable file
137
Build/lib/modules/Hyperion/00_stdlib.kmod
Executable file
@@ -0,0 +1,137 @@
|
|||||||
|
function string.hasSuffix(str, suffix)
|
||||||
|
return string.sub(str, #suffix+1) == suffix
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.hasPrefix(str, prefix)
|
||||||
|
return string.sub(str, 1, #prefix) == prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.getSuffix(str, prefix)
|
||||||
|
return string.sub(str, #prefix+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.getPrefix(str, suffix)
|
||||||
|
return string.sub(str, 1, #suffix)
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.join(str, ...)
|
||||||
|
return table.concat(table.pack(str, ...))
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.delim(str, ...)
|
||||||
|
return table.concat(table.pack(...), str)
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.split(str, delim, maxResultCountOrNil)
|
||||||
|
assert(#delim == 1, "only delim len 1 supported for now")
|
||||||
|
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
|
||||||
|
local rv = {}
|
||||||
|
local buf = ""
|
||||||
|
for i = 1, #str do
|
||||||
|
local c = string.sub(str,i,i)
|
||||||
|
if #rv ~= maxResultCountOrNil and c == delim then
|
||||||
|
table.insert(rv, buf)
|
||||||
|
buf = ""
|
||||||
|
else
|
||||||
|
buf = buf..c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(rv, buf)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.replace(str, search, replacement)
|
||||||
|
local rv = ""
|
||||||
|
local consumedLen = 1
|
||||||
|
local i = 1
|
||||||
|
while i<#str do
|
||||||
|
if string.sub(str, i, i+#search-1) == search then
|
||||||
|
rv = rv .. string.sub(str, consumedLen, i-1) .. replacement
|
||||||
|
i=i+#search
|
||||||
|
consumedLen = i
|
||||||
|
end
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
return rv .. string.sub(str, consumedLen)
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.deepcopy(orig, copies)
|
||||||
|
copies = copies or {}
|
||||||
|
|
||||||
|
if type(orig) ~= 'table' then
|
||||||
|
return orig
|
||||||
|
elseif copies[orig] then
|
||||||
|
return copies[orig]
|
||||||
|
end
|
||||||
|
|
||||||
|
local copy = {}
|
||||||
|
copies[orig] = copy
|
||||||
|
|
||||||
|
for k, v in next, orig, nil do
|
||||||
|
local copied_key = table.deepcopy(k, copies)
|
||||||
|
local copied_val = table.deepcopy(v, copies)
|
||||||
|
copy[copied_key] = copied_val
|
||||||
|
end
|
||||||
|
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.hasKey(tabl, query)
|
||||||
|
for i,v in pairs(tabl) do
|
||||||
|
if i==query then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.hasVal(tabl, query)
|
||||||
|
for i,v in pairs(tabl) do
|
||||||
|
if v==query then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function serialize(table)
|
||||||
|
local output = "{"
|
||||||
|
for i,v in pairs(table) do
|
||||||
|
local coma=true
|
||||||
|
if type(i) == "string" then
|
||||||
|
output=output.."[\""..i.."\"]="
|
||||||
|
end
|
||||||
|
if type(v) == "table" then
|
||||||
|
if v == table then
|
||||||
|
output=string.sub(output,1,#output-(#i+1))
|
||||||
|
coma=false
|
||||||
|
else
|
||||||
|
output=output..serialize(v)
|
||||||
|
end
|
||||||
|
elseif type(v) == "string" then
|
||||||
|
output=output.."[=["..v.."]=]"
|
||||||
|
elseif type(v) == "number" then
|
||||||
|
output=output..tostring(v)
|
||||||
|
elseif type(v) == "boolean" then
|
||||||
|
if v == true then
|
||||||
|
output=output.."true"
|
||||||
|
else
|
||||||
|
output=output.."false"
|
||||||
|
end
|
||||||
|
elseif type(v) == "function" then
|
||||||
|
output=output.."function() end"
|
||||||
|
else
|
||||||
|
error("serialization of type \""..type(v).."\" is not supported")
|
||||||
|
end
|
||||||
|
if coma then
|
||||||
|
output=output..","
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #table>0 or string.sub(output,#output,#output) == "," then
|
||||||
|
output=string.sub(output,1,#output-1)
|
||||||
|
end
|
||||||
|
output=output.."}"
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
table.serialize=serialize
|
||||||
217
Build/lib/modules/Hyperion/10_vfs.kmod
Executable file
217
Build/lib/modules/Hyperion/10_vfs.kmod
Executable file
@@ -0,0 +1,217 @@
|
|||||||
|
local args = {...}
|
||||||
|
local kernel = args[1]
|
||||||
|
local fs = {}
|
||||||
|
local disks = {}
|
||||||
|
local mounts = {["/"]="$"}
|
||||||
|
|
||||||
|
-- path normaliing
|
||||||
|
local function normalizePath(path)
|
||||||
|
if not path or path == "" then return "/" end
|
||||||
|
if path:sub(1,1) ~= "/" then path = "/" .. path end
|
||||||
|
local parts = {}
|
||||||
|
for part in path:gmatch("[^/]+") do
|
||||||
|
if part == ".." then
|
||||||
|
if #parts > 0 then table.remove(parts) end
|
||||||
|
elseif part ~= "." and part ~= "" then
|
||||||
|
parts[#parts+1] = part
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return "/" .. table.concat(parts, "/")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- disk resolution
|
||||||
|
local function resolvePath(path)
|
||||||
|
local abs = normalizePath(path)
|
||||||
|
local best = "/"
|
||||||
|
for mount, diskAddr in pairs(mounts) do
|
||||||
|
if abs:sub(1, #mount) == mount and #mount > #best then
|
||||||
|
best = mount
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local newPath = abs:sub(#best + 1)
|
||||||
|
if newPath == "" then newPath = "/" end
|
||||||
|
return disks[mounts[best]], newPath
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Symlinks
|
||||||
|
-- Format: #!@SYMLINK[target]
|
||||||
|
local SYMLINK_PREFIX = "#!@SYMLINK["
|
||||||
|
local SYMLINK_SUFFIX = "]"
|
||||||
|
local SYMLINK_MAX_DEPTH = 64
|
||||||
|
|
||||||
|
local function isSymlink(disk, path)
|
||||||
|
if not disk:fileExists(path) then return false end
|
||||||
|
local text = disk:readAllText(path)
|
||||||
|
if not text then return false end
|
||||||
|
return text:sub(1, #SYMLINK_PREFIX) == SYMLINK_PREFIX
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readSymlink(disk, path)
|
||||||
|
local text = disk:readAllText(path)
|
||||||
|
if not text then return nil end
|
||||||
|
if text:sub(1, #SYMLINK_PREFIX) ~= SYMLINK_PREFIX then return nil end
|
||||||
|
local target = text:sub(#SYMLINK_PREFIX + 1)
|
||||||
|
if target:sub(-1) == SYMLINK_SUFFIX then
|
||||||
|
target = target:sub(1, -2)
|
||||||
|
end
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Path resolution
|
||||||
|
local function splitPath(p)
|
||||||
|
local parts = {}
|
||||||
|
for part in p:gmatch("[^/]+") do parts[#parts+1] = part end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
|
||||||
|
local function resolveSymlink(path)
|
||||||
|
path = normalizePath(path)
|
||||||
|
local symlinkDepth = 0
|
||||||
|
local parts = splitPath(path)
|
||||||
|
local resolved = {}
|
||||||
|
local i = 1
|
||||||
|
|
||||||
|
while i <= #parts do
|
||||||
|
local comp = parts[i]
|
||||||
|
if comp == "." then
|
||||||
|
elseif comp == ".." then
|
||||||
|
if #resolved > 0 then table.remove(resolved) end
|
||||||
|
else
|
||||||
|
local currentPath = "/" .. table.concat(resolved, "/")
|
||||||
|
local disk, p = resolvePath(currentPath .. "/" .. comp)
|
||||||
|
if isSymlink(disk, p) then
|
||||||
|
symlinkDepth = symlinkDepth + 1
|
||||||
|
if symlinkDepth > SYMLINK_MAX_DEPTH then
|
||||||
|
error("Too many levels of symbolic links: " .. path)
|
||||||
|
end
|
||||||
|
local target = readSymlink(disk, p)
|
||||||
|
if not target then
|
||||||
|
resolved[#resolved+1] = comp
|
||||||
|
else
|
||||||
|
if target:sub(1,1) == "/" then
|
||||||
|
resolved = splitPath(normalizePath(target))
|
||||||
|
else
|
||||||
|
local base = resolved
|
||||||
|
resolved = {}
|
||||||
|
for _, seg in ipairs(base) do resolved[#resolved+1]=seg end
|
||||||
|
for seg in target:gmatch("[^/]+") do
|
||||||
|
if seg == ".." then
|
||||||
|
if #resolved>0 then table.remove(resolved) end
|
||||||
|
elseif seg ~= "." then
|
||||||
|
resolved[#resolved+1]=seg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local remaining = {}
|
||||||
|
for j=i+1,#parts do remaining[#remaining+1]=parts[j] end
|
||||||
|
parts = remaining
|
||||||
|
i = 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
resolved[#resolved+1]=comp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
|
||||||
|
local finalPath="/"..table.concat(resolved,"/")
|
||||||
|
local disk,diskPath=resolvePath(finalPath)
|
||||||
|
return disk,diskPath
|
||||||
|
end
|
||||||
|
|
||||||
|
-- PUBLIC API: MOUNTING
|
||||||
|
function fs.mount(disk, mountPoint)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
mounts[mountPoint]=disk
|
||||||
|
end
|
||||||
|
function fs.unmount(mountPoint)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
mounts[mountPoint]=nil
|
||||||
|
end
|
||||||
|
function fs.virtDisk(diskObj)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
if disks[diskObj.address] then error("Disk exists") end
|
||||||
|
disks[diskObj.address]=diskObj
|
||||||
|
end
|
||||||
|
function fs.eject(addr)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
disks[addr]=nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- SYMLINK API
|
||||||
|
function fs.symlink(target, linkPath)
|
||||||
|
local disk, p = resolvePath(linkPath)
|
||||||
|
return disk:writeAllText(p, SYMLINK_PREFIX..target..SYMLINK_SUFFIX)
|
||||||
|
end
|
||||||
|
function fs.isLink(path)
|
||||||
|
local disk, p = resolvePath(path)
|
||||||
|
return isSymlink(disk,p)
|
||||||
|
end
|
||||||
|
function fs.readLink(path)
|
||||||
|
local disk, p = resolvePath(path)
|
||||||
|
return readSymlink(disk,p)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- FILE OPERATIONS
|
||||||
|
function fs.exists(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:fileExists(p) or disk:directoryExists(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.isFile(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
if isSymlink(disk, p) then return false end
|
||||||
|
return disk:fileExists(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.isDir(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
if isSymlink(disk, p) then return false end
|
||||||
|
return disk:directoryExists(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.list(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:list(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.makeDir(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:makeDirectory(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.remove(path)
|
||||||
|
local disk, p = resolvePath(path)
|
||||||
|
if isSymlink(disk, p) then
|
||||||
|
return disk:remove(p)
|
||||||
|
end
|
||||||
|
local d2, p2 = resolveSymlink(path)
|
||||||
|
return d2:remove(p2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.readAllText(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:readAllText(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.writeAllText(path, text)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:writeAllText(p, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.appendAllText(path, text)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:appendAllText(p, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.getSize(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:getSize(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- INIT
|
||||||
|
for _,v in kernel.initdisks.list() do fs.virtDisk(v) end
|
||||||
|
kernel.fs=fs
|
||||||
|
kernel.chache.preload.fs=fs
|
||||||
|
kernel.chache.preload.filesystem=fs
|
||||||
22
Build/lib/modules/Hyperion/11_fstabInit.kmod
Executable file
22
Build/lib/modules/Hyperion/11_fstabInit.kmod
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
local ifs=kernel.ifs
|
||||||
|
local initdisks=kernel.initdisks
|
||||||
|
|
||||||
|
local fstab=ifs.readAllText("/etc/fstab")
|
||||||
|
kernel.fs.update(initdisks)
|
||||||
|
for i,v in ipairs(string.split(fstab,"\n")) do
|
||||||
|
if v:sub(1,1)=="U" then
|
||||||
|
local id=""
|
||||||
|
for i=3,#v do
|
||||||
|
if v:sub(i,i)==";" then
|
||||||
|
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
|
||||||
|
id=v:sub(3,i-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local path=v:sub(#id+4)
|
||||||
|
kernel.log("Mounted "..id.." to "..path)
|
||||||
|
kernel.fs.mount(id,path)
|
||||||
|
::endline::
|
||||||
|
end
|
||||||
|
end
|
||||||
182
Build/lib/modules/Hyperion/11_mkvirtfs.kmod
Executable file
182
Build/lib/modules/Hyperion/11_mkvirtfs.kmod
Executable file
@@ -0,0 +1,182 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
|
||||||
|
function kernel.fs.mkvirtfs(address, readOnly, label, autoRegister)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
local disk = {}
|
||||||
|
disk.address = address
|
||||||
|
local isRO = readOnly or false
|
||||||
|
|
||||||
|
local backend = {
|
||||||
|
["/"] = { __dir = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
-- Helpers
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
local function norm(path)
|
||||||
|
if not path or path == "" then return "/" end
|
||||||
|
if path:sub(1,1) ~= "/" then path = "/" .. path end
|
||||||
|
path = path:gsub("/+", "/")
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
local function split(path)
|
||||||
|
local parts = {}
|
||||||
|
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getNode(path)
|
||||||
|
path = norm(path)
|
||||||
|
if path == "/" then return backend["/"] end
|
||||||
|
local node = backend["/"]
|
||||||
|
for _, part in ipairs(split(path)) do
|
||||||
|
node = node[part]
|
||||||
|
if not node then return nil end
|
||||||
|
end
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getParent(path)
|
||||||
|
local parts = split(norm(path))
|
||||||
|
local name = table.remove(parts)
|
||||||
|
local parentPath = "/" .. table.concat(parts, "/")
|
||||||
|
return getNode(parentPath), name
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ensureWrite()
|
||||||
|
if isRO then return false, "read-only" end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
-- File IO (supports string or file-object with read/write funcs)
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
local function fileRead(node)
|
||||||
|
if type(node) == "string" then
|
||||||
|
return node
|
||||||
|
elseif type(node) == "table" and node.__file and node.read then
|
||||||
|
return node.read()
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fileWrite(parent, name, text)
|
||||||
|
local node = parent[name]
|
||||||
|
if type(node) == "string" then
|
||||||
|
parent[name] = text
|
||||||
|
return true
|
||||||
|
elseif type(node) == "table" and node.__file and node.write then
|
||||||
|
return node.write(text)
|
||||||
|
end
|
||||||
|
parent[name] = text
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fileAppend(parent, name, text)
|
||||||
|
local node = parent[name]
|
||||||
|
if type(node) == "string" then
|
||||||
|
parent[name] = node .. text
|
||||||
|
return true
|
||||||
|
elseif type(node) == "table" and node.__file and node.write then
|
||||||
|
return node.write((node.read() or "") .. text)
|
||||||
|
end
|
||||||
|
parent[name] = text
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
-- API
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
function disk:isReadOnly() return isRO end
|
||||||
|
function disk:spaceUsed() return 0 end
|
||||||
|
function disk:spaceTotal() return 0 end
|
||||||
|
|
||||||
|
function disk:readAllText(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
if not node then return nil, "file not found" end
|
||||||
|
return fileRead(node)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:writeAllText(path, text)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
|
||||||
|
local parent, name = getParent(path)
|
||||||
|
if not parent or not parent.__dir then return false, "invalid directory" end
|
||||||
|
return fileWrite(parent, name, tostring(text))
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:appendAllText(path, text)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
|
||||||
|
local parent, name = getParent(path)
|
||||||
|
if not parent or not parent.__dir then return false, "invalid directory" end
|
||||||
|
return fileAppend(parent, name, tostring(text))
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:list(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
if not node or not node.__dir then return nil, "not a directory" end
|
||||||
|
local out = {}
|
||||||
|
for k, _ in pairs(node) do if k ~= "__dir" then table.insert(out, k) end end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:fileExists(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
return type(node) == "string" or (type(node) == "table" and node.__file)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:directoryExists(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
return node and type(node) == "table" and node.__dir or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:makeDirectory(path)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
local parent, name = getParent(path)
|
||||||
|
if not parent or not parent.__dir then return false, "invalid parent directory" end
|
||||||
|
parent[name] = { __dir = true }
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:remove(path)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
local parent, name = getParent(path)
|
||||||
|
if not parent or not parent[name] then return false, "not found" end
|
||||||
|
parent[name] = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:setLabel(new)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
label = tostring(new)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:getLabel()
|
||||||
|
return label
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:size(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
if type(node) == "string" then return #node end
|
||||||
|
if type(node) == "table" and node.__file and node.read then
|
||||||
|
local v = node.read()
|
||||||
|
return v and #v or 0
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
-- Auto-register with kernel and return backend
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
if kernel and kernel.fs and kernel.fs.virtDisk and autoRegister then
|
||||||
|
kernel.fs.virtDisk(disk)
|
||||||
|
end
|
||||||
|
|
||||||
|
return backend, disk
|
||||||
|
end
|
||||||
56
Build/lib/modules/Hyperion/11_require.kmod
Executable file
56
Build/lib/modules/Hyperion/11_require.kmod
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
local args = {...}
|
||||||
|
local kernel = args[1]
|
||||||
|
|
||||||
|
-- List of search paths
|
||||||
|
local paths = {
|
||||||
|
"/lib/?",
|
||||||
|
"/usr/lib/?",
|
||||||
|
"/usr/local/lib/?"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Custom require implementation
|
||||||
|
function require(module)
|
||||||
|
-- Return cached module if it already exists
|
||||||
|
if kernel.cache.preload[module] then
|
||||||
|
return kernel.cache.preload[module]
|
||||||
|
end
|
||||||
|
|
||||||
|
local err = {}
|
||||||
|
|
||||||
|
for _, path in ipairs(paths) do
|
||||||
|
-- Replace "?" with module name
|
||||||
|
local filePath = string.replace(path, "?", module)
|
||||||
|
|
||||||
|
-- Try to open file
|
||||||
|
local file = kernel.fs.isFile(filePath)
|
||||||
|
if file then
|
||||||
|
local content = kernel.fs.readAllText(filePath)
|
||||||
|
|
||||||
|
-- Load the module as Lua code
|
||||||
|
local chunk, loadErr = load(content, filePath)
|
||||||
|
if not chunk then
|
||||||
|
table.insert(err, "Error loading: " .. filePath .. ": " .. loadErr)
|
||||||
|
else
|
||||||
|
-- Execute the module. If the module returns a value, cache it.
|
||||||
|
local ok, result = xpcall(chunk, debug.traceback)
|
||||||
|
if not ok then
|
||||||
|
table.insert(err, "Error executing: "..filePath..": "..tostring(result))
|
||||||
|
else
|
||||||
|
if result ~= nil then
|
||||||
|
kernel.cache.preload[module] = result
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If module doesn't return anything, cache `true` like Lua does
|
||||||
|
kernel.cache.preload[module] = true
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(err, "Module not found: " .. filePath)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If nothing worked, raise an error with all reasons
|
||||||
|
error("Unable to require module '" .. module .. "':\n" .. table.concat(err, "\n"))
|
||||||
|
end
|
||||||
17
Build/lib/modules/Hyperion/12_devfs.kmod
Executable file
17
Build/lib/modules/Hyperion/12_devfs.kmod
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel = args[1]
|
||||||
|
|
||||||
|
local data = kernel.fs.mkvirtfs("devfs0000", true, "devfs", false)
|
||||||
|
|
||||||
|
data["/"]["OSVERSION"]="Hyperion 1.0.0"
|
||||||
|
data["/"]["eeprom"]={
|
||||||
|
__file=true,
|
||||||
|
read=function()
|
||||||
|
return kernel.computer:getEEPROM()
|
||||||
|
end,
|
||||||
|
write=function(text)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
kernel.computer:setEEPROM(text)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
12
Build/lib/modules/Hyperion/20_keventd.kmod
Executable file
12
Build/lib/modules/Hyperion/20_keventd.kmod
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
kernel.drivers.processes[#kernel.drivers.processes+1]=function()
|
||||||
|
while true do
|
||||||
|
local event={kernel.computer:getMachineEvent()}
|
||||||
|
while event[1]~=nil do
|
||||||
|
kernel.hpv.sendSig("all", table.unpack(event))
|
||||||
|
event={kernel.computer:getMachineEvent()}
|
||||||
|
end
|
||||||
|
coroutine.yield()
|
||||||
|
end
|
||||||
|
end
|
||||||
19
Build/lib/modules/Hyperion/30_thirdParty.kmod
Normal file
19
Build/lib/modules/Hyperion/30_thirdParty.kmod
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[2]
|
||||||
|
|
||||||
|
for _,subf in ipairs(kernel.fs.list("/lib/modules/")) do
|
||||||
|
if subf~="Hyperion" then
|
||||||
|
for _,driver in ipairs(kernel.fs.list("/lib/modules/"..subf)) do
|
||||||
|
local code = kernel.fs.readAllText("/lib/modules/"..subf.."/"..driver)
|
||||||
|
local func, err = load(code, "@"..driver)
|
||||||
|
if not func then
|
||||||
|
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
|
||||||
|
else
|
||||||
|
local ok, err = xpcall(func, debug.traceback)
|
||||||
|
if not ok then
|
||||||
|
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
11
Build/lib/modules/Hyperion/31_thirdPartyInit.kmod
Executable file
11
Build/lib/modules/Hyperion/31_thirdPartyInit.kmod
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
|
||||||
|
for _,l in ipairs(kernel.drivers.prior) do
|
||||||
|
for _,d in ipairs(l) do
|
||||||
|
if d.init then
|
||||||
|
local ok,err = xpcall(d.init, debug.traceback)
|
||||||
|
if not ok then kernel.log("DriverInitErr: "..tostring(err)) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
0
Build/lib/modules/Hyperion/98_dbgProtect.kmod
Executable file
0
Build/lib/modules/Hyperion/98_dbgProtect.kmod
Executable file
167
Build/lib/modules/Hyperion/99_init.kmod
Executable file
167
Build/lib/modules/Hyperion/99_init.kmod
Executable file
@@ -0,0 +1,167 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel = args[1]
|
||||||
|
local tasks={}
|
||||||
|
local currentTask={}
|
||||||
|
local signals={}
|
||||||
|
local tid=1
|
||||||
|
local gid=1
|
||||||
|
local sys={}
|
||||||
|
|
||||||
|
function sys.hookSig(sig, func)
|
||||||
|
if not signals[tostring(currentTask.pid)][sig] then
|
||||||
|
signals[tostring(currentTask.pid)][sig]={}
|
||||||
|
end
|
||||||
|
signals[tostring(currentTask.pid)][sig][#signals[tostring(currentTask.pid)][sig]+1]=func
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.clearSigHooks(typ)
|
||||||
|
if not typ or typ == "all" then
|
||||||
|
signals[tostring(currentTask.pid)]={}
|
||||||
|
currentTask.signal=signals[tostring(currentTask.pid)]
|
||||||
|
else
|
||||||
|
if currentTask.signal[typ] then
|
||||||
|
currentTask.signal[typ]={}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.sendSig(pid, signal, ...)
|
||||||
|
if pid=="all" then
|
||||||
|
for i,v in pairs(tasks) do
|
||||||
|
v.sigQ[#v.sigQ+1]={signal, ...}
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not tasks[tostring(pid)] then return false end
|
||||||
|
tasks[tostring(pid)].sigQ[#tasks[tostring(pid)].sigQ+1]={signal, ...}
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.flushSigs()
|
||||||
|
local ret = {}
|
||||||
|
for i=1, #currentTask.sigQ do
|
||||||
|
if currentTask.signal[currentTask.sigQ[i][1]] then
|
||||||
|
for _,v in ipairs(currentTask.signal[currentTask.sigQ[1][1]]) do
|
||||||
|
coroutine.resumeWithTimeout(coroutine.create(function()
|
||||||
|
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
|
||||||
|
if not ok then
|
||||||
|
table.insert(ret, err)
|
||||||
|
end
|
||||||
|
end), 10)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for _,v in ipairs(currentTask.signal["unhandledEvent"]) do
|
||||||
|
coroutine.resumeWithTimeout(coroutine.create(function()
|
||||||
|
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
|
||||||
|
if not ok then
|
||||||
|
table.insert(ret, err)
|
||||||
|
end
|
||||||
|
end), 10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.spawn(func, name, evars, args)
|
||||||
|
local id=tid
|
||||||
|
tid=tid+1
|
||||||
|
name=name or tostring(id)
|
||||||
|
|
||||||
|
signals[tostring(id)]={}
|
||||||
|
tasks[tostring(id)]={
|
||||||
|
coro=coroutine.create(function()
|
||||||
|
local ret = {xpcall(func, debug.traceback, table.unpack(args))}
|
||||||
|
if not ret[1] then
|
||||||
|
sys.sendSig(currentTask.ppid, "ChildTaskError", id, err)
|
||||||
|
else
|
||||||
|
sys.sendSig(currentTask.ppid, "ChildTaskExit", id, table.unpack(ret, 2))
|
||||||
|
end
|
||||||
|
currentTask.status="Z"
|
||||||
|
end),
|
||||||
|
name=name,
|
||||||
|
pid=id,
|
||||||
|
ppid=currentTask.pid,
|
||||||
|
tgid=currentTask.tgid,
|
||||||
|
user=kernel.user,
|
||||||
|
uid=kernel.uid,
|
||||||
|
evars=evars,
|
||||||
|
args=args,
|
||||||
|
vy=0,
|
||||||
|
ivy=0,
|
||||||
|
status="R",
|
||||||
|
signal=signals[tostring(id)],
|
||||||
|
parent=currentTask,
|
||||||
|
children={},
|
||||||
|
sibling=currentTask.children,
|
||||||
|
sigQ={}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function collectZombieProc()
|
||||||
|
local ret = {}
|
||||||
|
for _,v in pairs(tasks) do
|
||||||
|
if v.status=="Z" then
|
||||||
|
local pid = v.pid
|
||||||
|
for _,c in ipairs(v.children) do
|
||||||
|
c.parent=tasks["1"]
|
||||||
|
c.sibling=tasks["1"].children
|
||||||
|
c.ppid=1
|
||||||
|
c.tgid=1
|
||||||
|
end
|
||||||
|
signals[tostring(pid)]=nil
|
||||||
|
tasks[tostring(pid)]=nil
|
||||||
|
table.insert(ret, pid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
signals["1"]={}
|
||||||
|
tasks["1"]={
|
||||||
|
coro=coroutine.create(function()
|
||||||
|
local ret = {xpcall(kernel.fs.load(kernel.initPath), debug.traceback)}
|
||||||
|
if not ok then
|
||||||
|
kernel.panic(err)
|
||||||
|
else
|
||||||
|
kernel.panic("Attempted to kill init!")
|
||||||
|
end
|
||||||
|
end),
|
||||||
|
name="SysInit",
|
||||||
|
pid=1,
|
||||||
|
ppid=0,
|
||||||
|
tgid=1,
|
||||||
|
user=kernel.user,
|
||||||
|
uid=kernel.uid,
|
||||||
|
evars={},
|
||||||
|
args={kernel},
|
||||||
|
vy=0,
|
||||||
|
ivy=0,
|
||||||
|
status="R",
|
||||||
|
signal=signals["1"],
|
||||||
|
parent={name="Hyprkrnl",pid=0},
|
||||||
|
children={},
|
||||||
|
sibling={},
|
||||||
|
sigQ={}
|
||||||
|
}
|
||||||
|
kernel.chache.preload.sys=sys
|
||||||
|
kernel.chache.preload.system=sys
|
||||||
|
kernel.hpv=sys
|
||||||
|
|
||||||
|
kernel.saveLog()
|
||||||
|
while true do
|
||||||
|
for _,v in pairs(tasks) do
|
||||||
|
currentTask=v
|
||||||
|
kernel.user=currentTask.user
|
||||||
|
kernel.uid=currentTask.uid
|
||||||
|
sys.flushSigs()
|
||||||
|
local status = coroutine.resumeWithTimeout(currentTask.coro, 50)
|
||||||
|
if status then
|
||||||
|
currentTask.vy=currentTask.vy+1
|
||||||
|
else
|
||||||
|
currentTask.ivy=currentTask.ivy+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
collectZombieProc()
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.panic("Exited pid 0")
|
||||||
109
Build/lib/modules/cct-tto.kmod
Executable file
109
Build/lib/modules/cct-tto.kmod
Executable file
@@ -0,0 +1,109 @@
|
|||||||
|
local args={...}
|
||||||
|
local apis=args[1]
|
||||||
|
local drivers=args[2]
|
||||||
|
local driver={}
|
||||||
|
|
||||||
|
driver.name="CCT TTO Module"
|
||||||
|
driver.version="0.1.0"
|
||||||
|
driver.description="CCT TTO Module Kernel Module"
|
||||||
|
driver.arch="cct"
|
||||||
|
driver.author="HyperionOS Dev Team"
|
||||||
|
driver.license="MIT"
|
||||||
|
driver.api={}
|
||||||
|
|
||||||
|
colors={
|
||||||
|
0x000000, -- #000000
|
||||||
|
0xFFFFFF, -- #FFFFFF
|
||||||
|
0xFF0000, -- #FF0000
|
||||||
|
0x00FF00, -- #00FF00
|
||||||
|
0x0000FF, -- #0000FF
|
||||||
|
0x00FFFF, -- #00FFFF
|
||||||
|
0xFF00FF, -- #FF00FF
|
||||||
|
0xFFFF00, -- #FFFF00
|
||||||
|
0xFF6D00, -- #FF6D00
|
||||||
|
0x6DFF55, -- #6DFF55
|
||||||
|
0x24FFFF, -- #24FFFF
|
||||||
|
0x924900, -- #924900
|
||||||
|
0x6D6D55, -- #6D6D55
|
||||||
|
0xDBDBAA, -- #DBDBAA
|
||||||
|
0x6D00FF, -- #6D00FF
|
||||||
|
0xB6FF00 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
icolors={
|
||||||
|
[0x1] =0, -- #000000
|
||||||
|
[0x2] =1, -- #FFFFFF
|
||||||
|
[0x4] =2, -- #FF0000
|
||||||
|
[0x8] =3, -- #00FF00
|
||||||
|
[0x10] =4, -- #0000FF
|
||||||
|
[0x20] =5, -- #00FFFF
|
||||||
|
[0x40] =6, -- #FF00FF
|
||||||
|
[0x80] =7, -- #FFFF00
|
||||||
|
[0x100] =8, -- #FF6D00
|
||||||
|
[0x200] =9, -- #6DFF55
|
||||||
|
[0x400] =10, -- #24FFFF
|
||||||
|
[0x800] =11, -- #924900
|
||||||
|
[0x1000] =12, -- #6D6D55
|
||||||
|
[0x2000] =13, -- #DBDBAA
|
||||||
|
[0x4000] =14, -- #6D00FF
|
||||||
|
[0x8000] =15 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
function driver.load()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.unload()
|
||||||
|
-- Nothing to unload
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.reload()
|
||||||
|
-- Nothing to reload
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.main()
|
||||||
|
-- Nothing to run
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.print(text)
|
||||||
|
apis.term.write(text.."\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.printInline(text)
|
||||||
|
apis.term.write(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.clear()
|
||||||
|
apis.term.clear()
|
||||||
|
apis.term.setCursorPos(1,1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.getSize()
|
||||||
|
return apis.term.getSize()
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.setBackgroundColor(index)
|
||||||
|
apis.term.setBackgroundColor(colors[index] or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.setForegroundColor(index)
|
||||||
|
apis.term.setTextColor(colors[index] or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.setCursorPos(x, y)
|
||||||
|
apis.term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.getCursorPos()
|
||||||
|
return apis.term.getCursorPos()
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.getBackgroundColor()
|
||||||
|
return icolors[apis.term.getBackgroundColor()]
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.getForegroundColor()
|
||||||
|
return icolors[apis.term.getTextColor()]
|
||||||
|
end
|
||||||
|
|
||||||
|
drivers.register(driver)
|
||||||
8
Build/sbin/init
Executable file
8
Build/sbin/init
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
|
||||||
|
kernel.panic("text")
|
||||||
|
|
||||||
|
for i,v in ipairs(kernel.fs.list("/dev")) do
|
||||||
|
kernl.log(v)
|
||||||
|
end
|
||||||
177
Src/iso.lua
Executable file
177
Src/iso.lua
Executable file
@@ -0,0 +1,177 @@
|
|||||||
|
-- Minimal ISO9660 parser
|
||||||
|
-- Provides: ISO.open(path) -> iso object
|
||||||
|
-- Methods: :close(), :readPVD(), :listRoot(), :readFile(path)
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local SECTOR_SIZE = 2048
|
||||||
|
|
||||||
|
local function readBytes(f, pos, len)
|
||||||
|
f:seek("set", pos)
|
||||||
|
return f:read(len)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function u8(s, i)
|
||||||
|
return string.byte(s, i)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function u16le(s, i)
|
||||||
|
local b1 = string.byte(s, i)
|
||||||
|
local b2 = string.byte(s, i+1)
|
||||||
|
return b1 + b2 * 256
|
||||||
|
end
|
||||||
|
|
||||||
|
local function u32le(s, i)
|
||||||
|
local b1 = string.byte(s, i)
|
||||||
|
local b2 = string.byte(s, i+1)
|
||||||
|
local b3 = string.byte(s, i+2)
|
||||||
|
local b4 = string.byte(s, i+3)
|
||||||
|
return b1 + b2*256 + b3*65536 + b4*16777216
|
||||||
|
end
|
||||||
|
|
||||||
|
local function trimVersion(name)
|
||||||
|
-- remove ;1 version and trailing dots
|
||||||
|
local n = name:gsub(";1$", "")
|
||||||
|
n = n:gsub("%.$", "")
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
|
local ISO = {}
|
||||||
|
ISO.__index = ISO
|
||||||
|
|
||||||
|
function M.open(path)
|
||||||
|
local f, err = io.open(path, "rb")
|
||||||
|
if not f then return nil, err end
|
||||||
|
local self = setmetatable({ f = f, path = path }, ISO)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function ISO:close()
|
||||||
|
if self.f then self.f:close(); self.f = nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ISO:readSector(n)
|
||||||
|
local pos = n * SECTOR_SIZE
|
||||||
|
return readBytes(self.f, pos, SECTOR_SIZE)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ISO:readPVD()
|
||||||
|
-- Primary Volume Descriptor at sector 16 (index 16)
|
||||||
|
local pvd = self:readSector(16)
|
||||||
|
if not pvd or #pvd < SECTOR_SIZE then return nil, "cannot read PVD" end
|
||||||
|
local typecode = u8(pvd,1)
|
||||||
|
local id = pvd:sub(2,6)
|
||||||
|
if typecode ~= 1 or id ~= "CD001" then return nil, "no primary volume descriptor" end
|
||||||
|
|
||||||
|
local volumeIdentifier = pvd:sub(41,72):gsub('%z+$','')
|
||||||
|
-- root directory record starts at offset 157 (1-based index)
|
||||||
|
local rdOffset = 157
|
||||||
|
local rdLen = u8(pvd, rdOffset)
|
||||||
|
local extent = u32le(pvd, rdOffset + 2)
|
||||||
|
local dataLen = u32le(pvd, rdOffset + 10)
|
||||||
|
|
||||||
|
self.pvd = {
|
||||||
|
volumeIdentifier = volumeIdentifier,
|
||||||
|
root = { extent = extent, size = dataLen }
|
||||||
|
}
|
||||||
|
return self.pvd
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parseDirRecords(buffer, bytesToRead)
|
||||||
|
local records = {}
|
||||||
|
local offset = 1
|
||||||
|
local total = bytesToRead or #buffer
|
||||||
|
while offset <= total do
|
||||||
|
local len = string.byte(buffer, offset)
|
||||||
|
if not len or len == 0 then
|
||||||
|
-- padding to sector boundary, move to next byte
|
||||||
|
offset = offset + 1
|
||||||
|
else
|
||||||
|
local rec = {}
|
||||||
|
rec.len = len
|
||||||
|
rec.extAttrLen = string.byte(buffer, offset+1)
|
||||||
|
rec.extent = u32le(buffer, offset+2)
|
||||||
|
rec.size = u32le(buffer, offset+10)
|
||||||
|
rec.recordingDate = buffer:sub(offset+18, offset+24)
|
||||||
|
rec.flags = string.byte(buffer, offset+25)
|
||||||
|
rec.fileUnitSize = string.byte(buffer, offset+26)
|
||||||
|
rec.interleaveGapSize = string.byte(buffer, offset+27)
|
||||||
|
rec.volumeSeq = u16le(buffer, offset+29)
|
||||||
|
local fiLen = string.byte(buffer, offset+32)
|
||||||
|
local idStart = offset + 33
|
||||||
|
local idEnd = idStart + fiLen - 1
|
||||||
|
local fileId = buffer:sub(idStart, idEnd)
|
||||||
|
-- strip trailing ;1 and dots
|
||||||
|
rec.fileIdentifier = fileId
|
||||||
|
rec.fileName = trimVersion(fileId)
|
||||||
|
rec.isDirectory = (rec.flags & 0x02) ~= 0
|
||||||
|
table.insert(records, rec)
|
||||||
|
offset = offset + len
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return records
|
||||||
|
end
|
||||||
|
|
||||||
|
function ISO:listRoot()
|
||||||
|
if not self.pvd then self:readPVD() end
|
||||||
|
local root = self.pvd.root
|
||||||
|
local extent = root.extent
|
||||||
|
local size = root.size
|
||||||
|
local pos = extent * SECTOR_SIZE
|
||||||
|
local buffer = readBytes(self.f, pos, size)
|
||||||
|
local records = parseDirRecords(buffer, size)
|
||||||
|
-- filter out '.' and '..' and entries with empty name
|
||||||
|
local out = {}
|
||||||
|
for _, r in ipairs(records) do
|
||||||
|
local name = r.fileName
|
||||||
|
if name and name ~= "" and name ~= "\0" and name ~= "\1" and name ~= "." and name ~= ".." then
|
||||||
|
table.insert(out, { name = name, isDirectory = r.isDirectory, extent = r.extent, size = r.size })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
local function splitPath(path)
|
||||||
|
local parts = {}
|
||||||
|
for part in path:gmatch('[^/\\]+') do parts[#parts+1]=part end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
|
||||||
|
function ISO:findEntryByPath(path)
|
||||||
|
if not self.pvd then self:readPVD() end
|
||||||
|
local parts = splitPath(path)
|
||||||
|
local curExtent = self.pvd.root.extent
|
||||||
|
local curSize = self.pvd.root.size
|
||||||
|
for i, part in ipairs(parts) do
|
||||||
|
-- read directory entries for curExtent
|
||||||
|
local buffer = readBytes(self.f, curExtent * SECTOR_SIZE, curSize)
|
||||||
|
local records = parseDirRecords(buffer, curSize)
|
||||||
|
local found = nil
|
||||||
|
for _, r in ipairs(records) do
|
||||||
|
local name = trimVersion(r.fileIdentifier)
|
||||||
|
if name:lower() == part:lower() then
|
||||||
|
found = r; break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not found then return nil, "path not found: " .. part end
|
||||||
|
if i < #parts then
|
||||||
|
if not found.isDirectory then return nil, "not a directory: "..part end
|
||||||
|
curExtent = found.extent
|
||||||
|
curSize = found.size
|
||||||
|
else
|
||||||
|
return found
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil, "empty path"
|
||||||
|
end
|
||||||
|
|
||||||
|
function ISO:readFile(path)
|
||||||
|
local rec, err = self:findEntryByPath(path)
|
||||||
|
if not rec then return nil, err end
|
||||||
|
if rec.isDirectory then return nil, "path is a directory" end
|
||||||
|
local pos = rec.extent * SECTOR_SIZE
|
||||||
|
return readBytes(self.f, pos, rec.size)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- return module
|
||||||
|
return M
|
||||||
10
Test/Hyperion-core-v0.1.0.pkg
Executable file
10
Test/Hyperion-core-v0.1.0.pkg
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"Name": "Hyperion core",
|
||||||
|
"Version": "0.1.0",
|
||||||
|
"Publishers": ["HyperionOS Dev Team"],
|
||||||
|
"Description": "Core system files and librarys for HyperionOS",
|
||||||
|
"Dependencies": [
|
||||||
|
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.pkg"
|
||||||
|
],
|
||||||
|
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-core-v0.1.0.tar"
|
||||||
|
}
|
||||||
2
Test/Hyperion-core-v0.1.0/etc/fstab
Executable file
2
Test/Hyperion-core-v0.1.0/etc/fstab
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
U $;/
|
||||||
|
U devfs0000;/dev/
|
||||||
3543
Test/Hyperion-core-v0.1.0/lib/LibDeflate
Executable file
3543
Test/Hyperion-core-v0.1.0/lib/LibDeflate
Executable file
File diff suppressed because it is too large
Load Diff
9
Test/Hyperion-core-v0.1.0/lib/deflate
Executable file
9
Test/Hyperion-core-v0.1.0/lib/deflate
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
local deflate=require("LibDeflate")
|
||||||
|
local lib={}
|
||||||
|
lib.compress=function(data)
|
||||||
|
return deflate:CompressDeflate(data)
|
||||||
|
end
|
||||||
|
lib.decompress=function(data)
|
||||||
|
return deflate:DecompressDeflate(data)
|
||||||
|
end
|
||||||
|
return lib
|
||||||
0
Test/Hyperion-core-v0.1.0/lib/globals/string
Executable file
0
Test/Hyperion-core-v0.1.0/lib/globals/string
Executable file
0
Test/Hyperion-core-v0.1.0/lib/globals/table
Executable file
0
Test/Hyperion-core-v0.1.0/lib/globals/table
Executable file
8
Test/Hyperion-core-v0.1.0/sbin/init
Executable file
8
Test/Hyperion-core-v0.1.0/sbin/init
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
|
||||||
|
kernel.panic("text")
|
||||||
|
|
||||||
|
for i,v in ipairs(kernel.fs.list("/dev")) do
|
||||||
|
kernl.log(v)
|
||||||
|
end
|
||||||
11
Test/Hyperion-firmware-cct-v0.1.0.pkg
Executable file
11
Test/Hyperion-firmware-cct-v0.1.0.pkg
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"Name": "Hyperion CCT Firmware",
|
||||||
|
"Version": "0.1.0",
|
||||||
|
"Publishers": ["HyperionOS Dev Team"],
|
||||||
|
"Description": "The ComputerCraftTweaked firmware package for HyperionOS.",
|
||||||
|
"Dependencies": [
|
||||||
|
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.pkg"
|
||||||
|
],
|
||||||
|
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-firmware-cct-v0.1.0.tar",
|
||||||
|
"BuildCommand": "sudo cp boot/cct/eeprom /dev/eeprom"
|
||||||
|
}
|
||||||
303
Test/Hyperion-firmware-cct-v0.1.0/boot/cct/boot.lua
Executable file
303
Test/Hyperion-firmware-cct-v0.1.0/boot/cct/boot.lua
Executable file
@@ -0,0 +1,303 @@
|
|||||||
|
local term = term
|
||||||
|
local os = os
|
||||||
|
-- Function to write text to the terminal with special character handling
|
||||||
|
local function write(text)
|
||||||
|
local x, y = term.getCursorPos()
|
||||||
|
local w, h = term.getSize()
|
||||||
|
|
||||||
|
for i = 1, #text do
|
||||||
|
local c = text:sub(i, i)
|
||||||
|
|
||||||
|
if c == "\n" then
|
||||||
|
y = y + 1
|
||||||
|
x = 1
|
||||||
|
elseif c == "\t" then
|
||||||
|
local tabSize = 4
|
||||||
|
local spaces = tabSize - ((x - 1) % tabSize)
|
||||||
|
term.write(string.rep(" ", spaces))
|
||||||
|
x = x + spaces
|
||||||
|
elseif c == "\b" then
|
||||||
|
if x > 1 then
|
||||||
|
x = x - 1
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
term.write(" ")
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if x <= w and y <= h then
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
term.write(c)
|
||||||
|
x = x + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle wrapping if we go past right edge
|
||||||
|
if x > w then
|
||||||
|
x = 1
|
||||||
|
y = y + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle scrolling if we go past bottom
|
||||||
|
if y-1 > h then
|
||||||
|
term.scroll(1)
|
||||||
|
y = h
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to display critical errors and halt the system
|
||||||
|
local function displaySuperBadError(err)
|
||||||
|
term.setBackgroundColor(0x1)
|
||||||
|
term.setTextColor(0x4)
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
term.write("A critical error occurred while loading the system:")
|
||||||
|
term.setCursorPos(1, 3)
|
||||||
|
write(err)
|
||||||
|
while true do end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Wrap all in xpcall to catch errors
|
||||||
|
local ok, err = xpcall(function()
|
||||||
|
local apis={}
|
||||||
|
|
||||||
|
-- List of standard Lua globals
|
||||||
|
local lua = {
|
||||||
|
coroutine = true,
|
||||||
|
debug = true,
|
||||||
|
_HOST = true,
|
||||||
|
_VERSION = true,
|
||||||
|
assert = true,
|
||||||
|
collectgarbage = true,
|
||||||
|
error = true,
|
||||||
|
gcinfo = true,
|
||||||
|
getfenv = true,
|
||||||
|
getmetatable = true,
|
||||||
|
ipairs = true,
|
||||||
|
__inext = true,
|
||||||
|
load = true,
|
||||||
|
math = true,
|
||||||
|
next = true,
|
||||||
|
pairs = true,
|
||||||
|
pcall = true,
|
||||||
|
rawequal = true,
|
||||||
|
rawget = true,
|
||||||
|
rawlen = true,
|
||||||
|
rawset = true,
|
||||||
|
select = true,
|
||||||
|
setfenv = true,
|
||||||
|
setmetatable = true,
|
||||||
|
string = true,
|
||||||
|
table = true,
|
||||||
|
tonumber = true,
|
||||||
|
tostring = true,
|
||||||
|
type = true,
|
||||||
|
xpcall = true,
|
||||||
|
_G=true
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Move all non-Lua standard library globals into the apis table
|
||||||
|
for i,v in pairs(_G) do
|
||||||
|
if not lua[i] or lua[i]==nil then
|
||||||
|
apis[i]=v
|
||||||
|
_G[i]=nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set up terminal default colors
|
||||||
|
apis.term.setPaletteColor(0x1, 0x000000) -- #000000
|
||||||
|
apis.term.setPaletteColor(0x2, 0xFFFFFF) -- #FFFFFF
|
||||||
|
apis.term.setPaletteColor(0x4, 0xFF0000) -- #FF0000
|
||||||
|
apis.term.setPaletteColor(0x8, 0x00FF00) -- #00FF00
|
||||||
|
apis.term.setPaletteColor(0x10, 0x0000FF) -- #0000FF
|
||||||
|
apis.term.setPaletteColor(0x20, 0x00FFFF) -- #00FFFF
|
||||||
|
apis.term.setPaletteColor(0x40, 0xFF00FF) -- #FF00FF
|
||||||
|
apis.term.setPaletteColor(0x80, 0xFFFF00) -- #FFFF00
|
||||||
|
apis.term.setPaletteColor(0x100, 0xFF6D00) -- #FF6D00
|
||||||
|
apis.term.setPaletteColor(0x200, 0x6DFF55) -- #6DFF55
|
||||||
|
apis.term.setPaletteColor(0x400, 0x24FFFF) -- #24FFFF
|
||||||
|
apis.term.setPaletteColor(0x800, 0x924900) -- #924900
|
||||||
|
apis.term.setPaletteColor(0x1000, 0x6D6D55) -- #6D6D55
|
||||||
|
apis.term.setPaletteColor(0x2000, 0xDBDBAA) -- #DBDBAA
|
||||||
|
apis.term.setPaletteColor(0x4000, 0x6D00FF) -- #6D00FF
|
||||||
|
apis.term.setPaletteColor(0x8000, 0xB6FF00) -- #B6FF00
|
||||||
|
|
||||||
|
-- Wrapper to read files
|
||||||
|
local function getFile(path)
|
||||||
|
local file = apis.fs.open(path, "r")
|
||||||
|
if not file then displaySuperBadError("Could not open file: "..path) end
|
||||||
|
local content = file.readAll()
|
||||||
|
file.close()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Load kernel first if it fails, we can't continue so we display an error
|
||||||
|
local Kernel = load(getFile("/$/boot/kernel.lua"),"@Kernel")
|
||||||
|
local initFs = load(getFile("/$/boot/cct/initdisks","@Init_disks"))(apis)
|
||||||
|
local fs = load(getFile("/$/boot/initfs"),"@InitFs")()
|
||||||
|
if not Kernel then
|
||||||
|
displaySuperBadError("Could not load kernel.")
|
||||||
|
end
|
||||||
|
if not initFs then
|
||||||
|
displaySuperBadError("Could not load initdisks.")
|
||||||
|
end
|
||||||
|
if not fs then
|
||||||
|
displaySuperBadError("Could not load initfs.")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set up event queue
|
||||||
|
local eventQueue = {}
|
||||||
|
local lkeys={}
|
||||||
|
lkeys[apis.keys.enter]="\n"
|
||||||
|
lkeys[apis.keys.backspace]="\b"
|
||||||
|
lkeys[apis.keys.tab]="\t"
|
||||||
|
|
||||||
|
-- Function to queue events
|
||||||
|
local function queueEvent(event, ...)
|
||||||
|
table.insert(eventQueue, {event, ...})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set up computer api
|
||||||
|
local computer = {
|
||||||
|
time = function() return apis.os.epoch("utc") end,
|
||||||
|
clock = apis.os.clock,
|
||||||
|
shutdown = apis.os.shutdown,
|
||||||
|
reboot = apis.os.reboot,
|
||||||
|
getMachineEvent = function()
|
||||||
|
if #eventQueue > 0 then
|
||||||
|
return table.unpack(table.remove(eventQueue, 1))
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
getEEPROM = function()
|
||||||
|
return getFile("/startup.lua")
|
||||||
|
end,
|
||||||
|
setEEPROM = function(_,text)
|
||||||
|
local h = apis.fs.open("/startup.lua", "w")
|
||||||
|
h.write(text)
|
||||||
|
h.close()
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Set up terminal colors
|
||||||
|
local icolors={
|
||||||
|
[0x1] =0, -- #000000
|
||||||
|
[0x2] =1, -- #FFFFFF
|
||||||
|
[0x4] =2, -- #FF0000
|
||||||
|
[0x8] =3, -- #00FF00
|
||||||
|
[0x10] =4, -- #0000FF
|
||||||
|
[0x20] =5, -- #00FFFF
|
||||||
|
[0x40] =6, -- #FF00FF
|
||||||
|
[0x80] =7, -- #FFFF00
|
||||||
|
[0x100] =8, -- #FF6D00
|
||||||
|
[0x200] =9, -- #6DFF55
|
||||||
|
[0x400] =10, -- #24FFFF
|
||||||
|
[0x800] =11, -- #924900
|
||||||
|
[0x1000] =12, -- #6D6D55
|
||||||
|
[0x2000] =13, -- #DBDBAA
|
||||||
|
[0x4000] =14, -- #6D00FF
|
||||||
|
[0x8000] =15 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
local colors={
|
||||||
|
[0]=0x0001, -- #000000
|
||||||
|
0x0002, -- #FFFFFF
|
||||||
|
0x0004, -- #FF0000
|
||||||
|
0x0008, -- #00FF00
|
||||||
|
0x0010, -- #0000FF
|
||||||
|
0x0020, -- #00FFFF
|
||||||
|
0x0040, -- #FF00FF
|
||||||
|
0x0080, -- #FFFF00
|
||||||
|
0x0100, -- #FF6D00
|
||||||
|
0x0200, -- #6DFF55
|
||||||
|
0x0400, -- #24FFFF
|
||||||
|
0x0800, -- #924900
|
||||||
|
0x1000, -- #6D6D55
|
||||||
|
0x2000, -- #DBDBAA
|
||||||
|
0x4000, -- #6D00FF
|
||||||
|
0x8000 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
apis.term.setBackgroundColor(0x1)
|
||||||
|
apis.term.setTextColor(0x1000)
|
||||||
|
apis.term.clear()
|
||||||
|
apis.term.setCursorPos(1, 1)
|
||||||
|
|
||||||
|
-- Make the kernel coroutine so we can hook its execution
|
||||||
|
local kernelCoro = coroutine.create(function()
|
||||||
|
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init", {
|
||||||
|
print=function(_,text) write(text.."\n") end,
|
||||||
|
printInline=function(_,text) write(text) end,
|
||||||
|
clear=function() apis.term.clear() apis.term.setCursorPos(1,1) end,
|
||||||
|
setCursorPos=function(_,x,y) apis.term.setCursorPos(x,y) end,
|
||||||
|
getCursorPos=function() return apis.term.getCursorPos() end,
|
||||||
|
getSize=function() return apis.term.getSize() end,
|
||||||
|
setBackgroundColor=function(_,color) apis.term.setBackgroundColor(colors[color]) end,
|
||||||
|
setTextColor=function(_,color) apis.term.setTextColor(colors[color]) end,
|
||||||
|
getBackgroundColor=function() return icolors[apis.term.getBackgroundColor()] end,
|
||||||
|
getTextColor=function() return icolors[apis.term.getTextColor()] end
|
||||||
|
}, computer, fs, "$")
|
||||||
|
if not ok then
|
||||||
|
displaySuperBadError(err)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Diffine a coroutine.resumeWithTimeout function to avoid hanging the system, time is in milliseconds
|
||||||
|
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||||
|
local startTime = computer.time()
|
||||||
|
debug.sethook(co, function()
|
||||||
|
if computer.time() > startTime + timeout then
|
||||||
|
return coroutine.yield("timeout")
|
||||||
|
end
|
||||||
|
end, "", 1000)
|
||||||
|
local ret = {coroutine.resume(co, ...)}
|
||||||
|
if ret[1]=="timeout" then
|
||||||
|
return nil, "Coroutine timed out"
|
||||||
|
elseif ret[1]==false then
|
||||||
|
return false, ret[2]
|
||||||
|
else
|
||||||
|
debug.sethook(co)
|
||||||
|
return true, table.unpack(ret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
write("Loaded in "..tostring(apis.os.clock()).." seconds.\n")
|
||||||
|
|
||||||
|
-- Main loop to run the kernel coroutine
|
||||||
|
while true do
|
||||||
|
local status, err = coroutine.resumeWithTimeout(kernelCoro, 50)
|
||||||
|
apis.os.queueEvent("NoSleep")
|
||||||
|
local exit = false
|
||||||
|
while not exit do
|
||||||
|
local event = {coroutine.yield()}
|
||||||
|
if event[1] == "char" then
|
||||||
|
queueEvent("keyTyped", 1, event[2])
|
||||||
|
elseif event[1] == "key" then
|
||||||
|
queueEvent("keyPressed", 1, event[2])
|
||||||
|
if lkeys[event[2]] then
|
||||||
|
queueEvent("keyTyped", 1, lkeys[event[2]])
|
||||||
|
end
|
||||||
|
elseif event[1] == "key_up" then
|
||||||
|
queueEvent("keyReleased", 1, event[2])
|
||||||
|
elseif event[1] == "disk" then
|
||||||
|
queueEvent("componentAdded", "disk")
|
||||||
|
elseif event[1] == "disk_eject" then
|
||||||
|
queueEvent("componentRemoved", "disk")
|
||||||
|
elseif event[1] == "NoSleep" then
|
||||||
|
exit=true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if status == false or coroutine.status(kernelCoro)=="dead" then
|
||||||
|
displaySuperBadError("Kernel error: "..tostring(err))
|
||||||
|
coroutine.yield("key")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end, debug.traceback)
|
||||||
|
if not ok then
|
||||||
|
displaySuperBadError("Fatal error during boot: "..err)
|
||||||
|
end
|
||||||
|
while true do coroutine.yield() end
|
||||||
119
Test/Hyperion-firmware-cct-v0.1.0/boot/cct/eeprom
Executable file
119
Test/Hyperion-firmware-cct-v0.1.0/boot/cct/eeprom
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
-- UnBIOS by JackMacWindows
|
||||||
|
-- This will undo most of the changes/additions made in the BIOS, but some things may remain wrapped if `debug` is unavailable
|
||||||
|
-- To use, just place a `bios.lua` in the root of the drive, and run this program
|
||||||
|
-- Here's a list of things that are irreversibly changed:
|
||||||
|
-- * both `bit` and `bit32` are kept for compatibility
|
||||||
|
-- * string metatable blocking (on old versions of CC)
|
||||||
|
-- In addition, if `debug` is not available these things are also irreversibly changed:
|
||||||
|
-- * old Lua 5.1 `load` function (for loading from a function)
|
||||||
|
-- * `loadstring` prefixing (before CC:T 1.96.0)
|
||||||
|
-- * `http.request`
|
||||||
|
-- * `os.shutdown` and `os.reboot`
|
||||||
|
-- * `peripheral`
|
||||||
|
-- * `turtle.equip[Left|Right]`
|
||||||
|
-- Licensed under the MIT license
|
||||||
|
local args = {...}
|
||||||
|
if _HOST:find("UnBIOS") then return end
|
||||||
|
local keptAPIs = {keys=true, bit32 = true, bit = true, ccemux = true, config = true, coroutine = true, debug = true, fs = true, http = true, mounter = true, os = true, periphemu = true, peripheral = true, redstone = true, rs = true, term = true, utf8 = true, _HOST = true, _CC_DEFAULT_SETTINGS = true, _CC_DISABLE_LUA51_FEATURES = true, _VERSION = true, assert = true, collectgarbage = true, error = true, gcinfo = true, getfenv = true, getmetatable = true, ipairs = true, __inext = true,load = true, loadstring = true, math = true, newproxy = true, next = true, pairs = true, pcall = true, rawequal = true, rawget = true, rawlen = true, rawset = true, select = true, setfenv = true, setmetatable = true, string = true, table = true, tonumber = true, tostring = true, type = true, unpack = true, xpcall = true, turtle = true, pocket = true, commands = true, _G = true}
|
||||||
|
local t = {}
|
||||||
|
for k in pairs(_G) do if not keptAPIs[k] then table.insert(t, k) end end
|
||||||
|
for _,k in ipairs(t) do _G[k] = nil end
|
||||||
|
local native = _G.term.native()
|
||||||
|
for _, method in ipairs {"nativePaletteColor", "nativePaletteColour", "screenshot"} do native[method] = _G.term[method] end
|
||||||
|
_G.term = native
|
||||||
|
if _G.http then
|
||||||
|
_G.http.checkURL = _G.http.checkURLAsync
|
||||||
|
_G.http.websocket = _G.http.websocketAsync
|
||||||
|
end
|
||||||
|
if _G.commands then _G.commands = _G.commands.native end
|
||||||
|
if _G.turtle then _G.turtle.native, _G.turtle.craft = nil end
|
||||||
|
local delete = {os = {"version", "pullEventRaw", "pullEvent", "run", "loadAPI", "unloadAPI", "sleep"}, http = _G.http and {"get", "post", "put", "delete", "patch", "options", "head", "trace", "listen", "checkURLAsync", "websocketAsync"}, fs = {"complete", "isDriveRoot"}}
|
||||||
|
for k,v in pairs(delete) do for _,a in ipairs(v) do _G[k][a] = nil end end
|
||||||
|
_G._HOST = _G._HOST .. " (UnBIOS)"
|
||||||
|
-- Set up TLCO
|
||||||
|
-- This functions by crashing `rednet.run` by removing `os.pullEventRaw`. Normally
|
||||||
|
-- this would cause `parallel` to throw an error, but we replace `error` with an
|
||||||
|
-- empty placeholder to let it continue and return without throwing. This results
|
||||||
|
-- in the `pcall` returning successfully, preventing the error-displaying code
|
||||||
|
-- from running - essentially making it so that `os.shutdown` is called immediately
|
||||||
|
-- after the new BIOS exits.
|
||||||
|
--
|
||||||
|
-- From there, the setup code is placed in `term.native` since it's the first
|
||||||
|
-- thing called after `parallel` exits. This loads the new BIOS and prepares it
|
||||||
|
-- for execution. Finally, it overwrites `os.shutdown` with the new function to
|
||||||
|
-- allow it to be the last function called in the original BIOS, and returns.
|
||||||
|
-- From there execution continues, calling the `term.redirect` dummy, skipping
|
||||||
|
-- over the error-handling code (since `pcall` returned ok), and calling
|
||||||
|
-- `os.shutdown()`. The real `os.shutdown` is re-added, and the new BIOS is tail
|
||||||
|
-- called, which effectively makes it run as the main chunk.
|
||||||
|
local olderror = error
|
||||||
|
_G.error = function() end
|
||||||
|
_G.term.redirect = function() end
|
||||||
|
function _G.term.native()
|
||||||
|
_G.term.native = nil
|
||||||
|
_G.term.redirect = nil
|
||||||
|
_G.error = olderror
|
||||||
|
term.setBackgroundColor(32768)
|
||||||
|
term.setTextColor(1)
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
term.setCursorBlink(true)
|
||||||
|
term.clear()
|
||||||
|
local file = fs.open("/$/boot/cct/boot.lua", "r")
|
||||||
|
if file == nil then
|
||||||
|
term.setCursorBlink(false)
|
||||||
|
term.setTextColor(16384)
|
||||||
|
term.write("Could not find /boot/cct/boot.lua. UnBIOS cannot continue.")
|
||||||
|
term.setCursorPos(1, 2)
|
||||||
|
term.write("Press any key to continue")
|
||||||
|
coroutine.yield("key")
|
||||||
|
os.shutdown()
|
||||||
|
end
|
||||||
|
local fn, err = loadstring(file.readAll(), "@bootloader")
|
||||||
|
file.close()
|
||||||
|
if fn == nil then
|
||||||
|
term.setCursorBlink(false)
|
||||||
|
term.setTextColor(16384)
|
||||||
|
term.write("Could not load /boot/cc/boot.lua. UnBIOS cannot continue.")
|
||||||
|
term.setCursorPos(1, 2)
|
||||||
|
term.write(err)
|
||||||
|
term.setCursorPos(1, 3)
|
||||||
|
term.write("Press any key to continue")
|
||||||
|
coroutine.yield("key")
|
||||||
|
os.shutdown()
|
||||||
|
end
|
||||||
|
setfenv(fn, _G)
|
||||||
|
local oldshutdown = os.shutdown
|
||||||
|
os.shutdown = function()
|
||||||
|
os.shutdown = oldshutdown
|
||||||
|
return fn(table.unpack(args))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if debug then
|
||||||
|
-- Restore functions that were overwritten in the BIOS
|
||||||
|
-- Apparently this has to be done *after* redefining term.native
|
||||||
|
local function restoreValue(tab, idx, name, hint)
|
||||||
|
local i, key, value = 1, debug.getupvalue(tab[idx], hint)
|
||||||
|
while key ~= name and key ~= nil do
|
||||||
|
key, value = debug.getupvalue(tab[idx], i)
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
tab[idx] = value or tab[idx]
|
||||||
|
end
|
||||||
|
restoreValue(_G, "loadstring", "nativeloadstring", 1)
|
||||||
|
restoreValue(_G, "load", "nativeload", 5)
|
||||||
|
if http then restoreValue(http, "request", "nativeHTTPRequest", 3) end
|
||||||
|
restoreValue(os, "shutdown", "nativeShutdown", 1)
|
||||||
|
restoreValue(os, "reboot", "nativeReboot", 1)
|
||||||
|
if turtle then
|
||||||
|
restoreValue(turtle, "equipLeft", "v", 1)
|
||||||
|
restoreValue(turtle, "equipRight", "v", 1)
|
||||||
|
end
|
||||||
|
do
|
||||||
|
local i, key, value = 1, debug.getupvalue(peripheral.isPresent, 2)
|
||||||
|
while key ~= "native" and key ~= nil do
|
||||||
|
key, value = debug.getupvalue(peripheral.isPresent, i)
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
_G.peripheral = value or peripheral
|
||||||
|
end
|
||||||
|
end
|
||||||
201
Test/Hyperion-firmware-cct-v0.1.0/boot/cct/initdisks
Executable file
201
Test/Hyperion-firmware-cct-v0.1.0/boot/cct/initdisks
Executable file
@@ -0,0 +1,201 @@
|
|||||||
|
local apis = ({...})[1]
|
||||||
|
local fs = apis.fs
|
||||||
|
local native = apis.peripheral
|
||||||
|
local peripheral = {}
|
||||||
|
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||||
|
|
||||||
|
function peripheral.getType(name)
|
||||||
|
if native.isPresent(name) then
|
||||||
|
return native.getType(name)
|
||||||
|
end
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||||
|
return native.call(side, "getTypeRemote", name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function peripheral.getNames()
|
||||||
|
local names = {}
|
||||||
|
for n = 1, #sides do
|
||||||
|
local side = sides[n]
|
||||||
|
if native.isPresent(side) then
|
||||||
|
table.insert(names, side)
|
||||||
|
end
|
||||||
|
if native.hasType(side, "peripheral_hub") then
|
||||||
|
local hubSides = native.call(side, "getConnectedSides")
|
||||||
|
for _, hubSide in ipairs(hubSides) do
|
||||||
|
table.insert(names, hubSide)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return names
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- STORAGE
|
||||||
|
---------------------------------------------------------
|
||||||
|
local disks = {} -- real disks
|
||||||
|
local internal = {} -- "$" disk
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- HELPERS
|
||||||
|
---------------------------------------------------------
|
||||||
|
local function norm(path)
|
||||||
|
if not path or path == "" then return "/" end
|
||||||
|
return fs.combine("/", path)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Creates a disk object given a base path
|
||||||
|
local function createDisk(id, basePath, readonly, periph)
|
||||||
|
basePath = norm(basePath)
|
||||||
|
|
||||||
|
local disk = {address=id,isReadOnly=function() return readonly end}
|
||||||
|
|
||||||
|
function disk:spaceUsed()
|
||||||
|
return fs.getCapacity(basePath) - fs.getFreeSpace(basePath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:spaceTotal()
|
||||||
|
return fs.getCapacity(basePath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:readAllText(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
if not fs.exists(p) then return nil, "file not found" end
|
||||||
|
local h = fs.open(p, "r")
|
||||||
|
local t = h.readAll()
|
||||||
|
h.close()
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:writeAllText(path, text)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
local h = fs.open(p, "w")
|
||||||
|
if not h then return nil, "cannot write" end
|
||||||
|
h.write(text)
|
||||||
|
h.close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:appendAllText(path, text)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
local h = fs.open(p, "a")
|
||||||
|
if not h then return nil, "cannot append" end
|
||||||
|
h.write(text)
|
||||||
|
h.close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:list(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
if not fs.exists(p) or not fs.isDir(p) then return nil, "not directory" end
|
||||||
|
return fs.list(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:fileExists(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.exists(p) and not fs.isDir(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:directoryExists(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.exists(p) and fs.isDir(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:makeDirectory(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
fs.makeDir(p)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:remove(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
if fs.exists(p) then fs.delete(p) end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:setLabel(label)
|
||||||
|
periph.setLabel(label)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:getLabel(label)
|
||||||
|
return periph.getLabel()
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:size(path)
|
||||||
|
local p = fs.combine(basePath, path)
|
||||||
|
return fs.getSize(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
return disk
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- INTERNAL DISK "$" (mapped to "/")
|
||||||
|
---------------------------------------------------------
|
||||||
|
internal["$"] = createDisk("$", "/$", false, {
|
||||||
|
setLabel=function(label)
|
||||||
|
local h = fs.open("/.label", "w")
|
||||||
|
h.write(label)
|
||||||
|
h.close()
|
||||||
|
end,
|
||||||
|
getLabel=function()
|
||||||
|
local h = fs.open("/.label", "r")
|
||||||
|
local label = h.readAll()
|
||||||
|
h.close()
|
||||||
|
return label
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- SCAN REAL DISK PERIPHERALS
|
||||||
|
---------------------------------------------------------
|
||||||
|
local function refresh()
|
||||||
|
-- remove disks that no longer exist
|
||||||
|
for id, _ in pairs(disks) do
|
||||||
|
if not peripheral.getType(id) then
|
||||||
|
disks[id] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- detect new disks
|
||||||
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
|
if peripheral.getType(name) == "disk" then
|
||||||
|
if not disks[name] then
|
||||||
|
local mount = disk.getMountPath(name)
|
||||||
|
if mount then
|
||||||
|
disks[name] = createDisk(name, mount, false, disk)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- ITERATOR
|
||||||
|
---------------------------------------------------------
|
||||||
|
local function iter()
|
||||||
|
refresh()
|
||||||
|
-- first internal
|
||||||
|
local combined = {}
|
||||||
|
|
||||||
|
for id, obj in pairs(internal) do
|
||||||
|
combined[id] = obj
|
||||||
|
end
|
||||||
|
for id, obj in pairs(disks) do
|
||||||
|
combined[id] = obj
|
||||||
|
end
|
||||||
|
|
||||||
|
return pairs(combined)
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
-- MODULE RETURN
|
||||||
|
---------------------------------------------------------
|
||||||
|
return {
|
||||||
|
refresh = refresh,
|
||||||
|
list = iter
|
||||||
|
}
|
||||||
109
Test/Hyperion-firmware-cct-v0.1.0/lib/modules/cct-tto.kmod
Executable file
109
Test/Hyperion-firmware-cct-v0.1.0/lib/modules/cct-tto.kmod
Executable file
@@ -0,0 +1,109 @@
|
|||||||
|
local args={...}
|
||||||
|
local apis=args[1]
|
||||||
|
local drivers=args[2]
|
||||||
|
local driver={}
|
||||||
|
|
||||||
|
driver.name="CCT TTO Module"
|
||||||
|
driver.version="0.1.0"
|
||||||
|
driver.description="CCT TTO Module Kernel Module"
|
||||||
|
driver.arch="cct"
|
||||||
|
driver.author="HyperionOS Dev Team"
|
||||||
|
driver.license="MIT"
|
||||||
|
driver.api={}
|
||||||
|
|
||||||
|
colors={
|
||||||
|
0x000000, -- #000000
|
||||||
|
0xFFFFFF, -- #FFFFFF
|
||||||
|
0xFF0000, -- #FF0000
|
||||||
|
0x00FF00, -- #00FF00
|
||||||
|
0x0000FF, -- #0000FF
|
||||||
|
0x00FFFF, -- #00FFFF
|
||||||
|
0xFF00FF, -- #FF00FF
|
||||||
|
0xFFFF00, -- #FFFF00
|
||||||
|
0xFF6D00, -- #FF6D00
|
||||||
|
0x6DFF55, -- #6DFF55
|
||||||
|
0x24FFFF, -- #24FFFF
|
||||||
|
0x924900, -- #924900
|
||||||
|
0x6D6D55, -- #6D6D55
|
||||||
|
0xDBDBAA, -- #DBDBAA
|
||||||
|
0x6D00FF, -- #6D00FF
|
||||||
|
0xB6FF00 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
icolors={
|
||||||
|
[0x1] =0, -- #000000
|
||||||
|
[0x2] =1, -- #FFFFFF
|
||||||
|
[0x4] =2, -- #FF0000
|
||||||
|
[0x8] =3, -- #00FF00
|
||||||
|
[0x10] =4, -- #0000FF
|
||||||
|
[0x20] =5, -- #00FFFF
|
||||||
|
[0x40] =6, -- #FF00FF
|
||||||
|
[0x80] =7, -- #FFFF00
|
||||||
|
[0x100] =8, -- #FF6D00
|
||||||
|
[0x200] =9, -- #6DFF55
|
||||||
|
[0x400] =10, -- #24FFFF
|
||||||
|
[0x800] =11, -- #924900
|
||||||
|
[0x1000] =12, -- #6D6D55
|
||||||
|
[0x2000] =13, -- #DBDBAA
|
||||||
|
[0x4000] =14, -- #6D00FF
|
||||||
|
[0x8000] =15 -- #B6FF00
|
||||||
|
}
|
||||||
|
|
||||||
|
function driver.load()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.unload()
|
||||||
|
-- Nothing to unload
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.reload()
|
||||||
|
-- Nothing to reload
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.main()
|
||||||
|
-- Nothing to run
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.print(text)
|
||||||
|
apis.term.write(text.."\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.printInline(text)
|
||||||
|
apis.term.write(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.clear()
|
||||||
|
apis.term.clear()
|
||||||
|
apis.term.setCursorPos(1,1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.getSize()
|
||||||
|
return apis.term.getSize()
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.setBackgroundColor(index)
|
||||||
|
apis.term.setBackgroundColor(colors[index] or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.setForegroundColor(index)
|
||||||
|
apis.term.setTextColor(colors[index] or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.setCursorPos(x, y)
|
||||||
|
apis.term.setCursorPos(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.getCursorPos()
|
||||||
|
return apis.term.getCursorPos()
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.getBackgroundColor()
|
||||||
|
return icolors[apis.term.getBackgroundColor()]
|
||||||
|
end
|
||||||
|
|
||||||
|
function driver.api.getForegroundColor()
|
||||||
|
return icolors[apis.term.getTextColor()]
|
||||||
|
end
|
||||||
|
|
||||||
|
drivers.register(driver)
|
||||||
11
Test/Hyperion-firmware-oc-v0.1.0.pkg
Executable file
11
Test/Hyperion-firmware-oc-v0.1.0.pkg
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"Name": "Hyperion OC Firmware",
|
||||||
|
"Version": "0.1.0",
|
||||||
|
"Publishers": ["HyperionOS Dev Team"],
|
||||||
|
"Description": "The OpenComputers firmware package for HyperionOS.",
|
||||||
|
"Dependencies": [
|
||||||
|
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.pkg"
|
||||||
|
],
|
||||||
|
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-firmware-oc-v0.1.0.tar",
|
||||||
|
"BuildCommand": "cp boot/oc/eeprom /dev/eeprom"
|
||||||
|
}
|
||||||
40
Test/Hyperion-firmware-oc-v0.1.0/boot/oc/boot.lua
Executable file
40
Test/Hyperion-firmware-oc-v0.1.0/boot/oc/boot.lua
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
local lua = {
|
||||||
|
coroutine = true,
|
||||||
|
debug = true,
|
||||||
|
_HOST = true,
|
||||||
|
_VERSION = true,
|
||||||
|
assert = true,
|
||||||
|
collectgarbage = true,
|
||||||
|
error = true,
|
||||||
|
gcinfo = true,
|
||||||
|
getfenv = true,
|
||||||
|
getmetatable = true,
|
||||||
|
ipairs = true,
|
||||||
|
__inext = true,
|
||||||
|
load = true,
|
||||||
|
math = true,
|
||||||
|
next = true,
|
||||||
|
pairs = true,
|
||||||
|
pcall = true,
|
||||||
|
rawequal = true,
|
||||||
|
rawget = true,
|
||||||
|
rawlen = true,
|
||||||
|
rawset = true,
|
||||||
|
select = true,
|
||||||
|
setfenv = true,
|
||||||
|
setmetatable = true,
|
||||||
|
string = true,
|
||||||
|
table = true,
|
||||||
|
tonumber = true,
|
||||||
|
tostring = true,
|
||||||
|
type = true,
|
||||||
|
xpcall = true,
|
||||||
|
_G=true
|
||||||
|
}
|
||||||
|
|
||||||
|
for i,v in pairs(_G) do
|
||||||
|
if not lua[i] or lua[i]==nil then
|
||||||
|
apis[i]=v
|
||||||
|
_G[i]=nil
|
||||||
|
end
|
||||||
|
end
|
||||||
18
Test/Hyperion-firmware-oc-v0.1.0/boot/oc/eeprom
Executable file
18
Test/Hyperion-firmware-oc-v0.1.0/boot/oc/eeprom
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
checkArg=nil
|
||||||
|
local oldcomputer=computer
|
||||||
|
_G.computer=nil
|
||||||
|
local os=os
|
||||||
|
_G.os=nil
|
||||||
|
|
||||||
|
function component.wrap(address)
|
||||||
|
local methods=oldcomponent.methods(address)
|
||||||
|
local object={}
|
||||||
|
for _,method in ipairs(methods) do
|
||||||
|
object[method]=function(_,...)
|
||||||
|
return oldcomponent.invoke(address,method,...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
local
|
||||||
1
Test/Hyperion-firmware-oc-v0.1.0/boot/oc/initfs.lua
Executable file
1
Test/Hyperion-firmware-oc-v0.1.0/boot/oc/initfs.lua
Executable file
@@ -0,0 +1 @@
|
|||||||
|
local fs={}
|
||||||
8
Test/Hyperion-kernel-v0.1.0.pkg
Executable file
8
Test/Hyperion-kernel-v0.1.0.pkg
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Name": "Hyperion Kernel",
|
||||||
|
"Version": "1.0.0",
|
||||||
|
"Publishers": ["HyperionOS Dev Team"],
|
||||||
|
"Description": "The kernel package for HyperionOS.",
|
||||||
|
"Dependencies": [],
|
||||||
|
"Tar": "https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.tar"
|
||||||
|
}
|
||||||
69
Test/Hyperion-kernel-v0.1.0/boot/initfs
Executable file
69
Test/Hyperion-kernel-v0.1.0/boot/initfs
Executable file
@@ -0,0 +1,69 @@
|
|||||||
|
local fs = {}
|
||||||
|
local disks = {}
|
||||||
|
local mounts = {}
|
||||||
|
|
||||||
|
local function resolve(path)
|
||||||
|
local mountPoint = "/"
|
||||||
|
for mount, disk in pairs(mounts) do
|
||||||
|
if path:sub(1, #mount) == mount then
|
||||||
|
if not mountPoint or #mount > #mountPoint then
|
||||||
|
mountPoint = mount
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local newPath = path:sub(#mountPoint + 1)
|
||||||
|
return disks[mounts[mountPoint]], newPath
|
||||||
|
end
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
function fs.update(initdisks)
|
||||||
|
disks = {}
|
||||||
|
for k, v in initdisks.list() do
|
||||||
|
disks[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.exists(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:directoryExists(newPath) or disk:fileExists(newPath)
|
||||||
|
end
|
||||||
|
function fs.isFile(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:fileExists(newPath)
|
||||||
|
end
|
||||||
|
function fs.isDir(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:directoryExists(newPath)
|
||||||
|
end
|
||||||
|
function fs.list(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:list(newPath)
|
||||||
|
end
|
||||||
|
function fs.makeDir(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:makeDirectory(newPath)
|
||||||
|
end
|
||||||
|
function fs.remove(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:remove(newPath)
|
||||||
|
end
|
||||||
|
function fs.readAllText(path)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:readAllText(newPath)
|
||||||
|
end
|
||||||
|
function fs.writeAllText(path, text)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:writeAllText(newPath, text)
|
||||||
|
end
|
||||||
|
function fs.appendAllText(path, text)
|
||||||
|
local disk, newPath = resolve(path)
|
||||||
|
return disk:appendAllText(newPath, text)
|
||||||
|
end
|
||||||
|
function fs.load(path)
|
||||||
|
return load(fs.readAllText(path), path)
|
||||||
|
end
|
||||||
|
function fs.mount(disk, mountPoint)
|
||||||
|
if not disks[disk] then return end
|
||||||
|
mounts[mountPoint] = disk
|
||||||
|
end
|
||||||
|
return fs
|
||||||
166
Test/Hyperion-kernel-v0.1.0/boot/kernel.lua
Executable file
166
Test/Hyperion-kernel-v0.1.0/boot/kernel.lua
Executable file
@@ -0,0 +1,166 @@
|
|||||||
|
local args = {...}
|
||||||
|
local apis = args[1]
|
||||||
|
local disks = args[2]
|
||||||
|
local arch = args[3]
|
||||||
|
local initPath = args[4]
|
||||||
|
local screen = args[5]
|
||||||
|
local computer = args[6]
|
||||||
|
local ifs = args[7]
|
||||||
|
local LOG_Text = ""
|
||||||
|
local kernel = {}
|
||||||
|
kernel.process = "Kernel"
|
||||||
|
kernel.user = "root"
|
||||||
|
kernel.group = "root"
|
||||||
|
kernel.groups = {0}
|
||||||
|
kernel.uid = 0
|
||||||
|
kernel.gid = 0
|
||||||
|
kernel.stage = "start"
|
||||||
|
kernel.key = {}
|
||||||
|
kernel.chache = {}
|
||||||
|
kernel.chache.preload = {}
|
||||||
|
local windowsExp = false
|
||||||
|
|
||||||
|
function kernel.log(msg, level)
|
||||||
|
LOG_Text = LOG_Text..tostring(computer.time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
||||||
|
if kernel.stage == "start" then
|
||||||
|
screen:print(tostring(computer.time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function kernel.PANIC(msg)
|
||||||
|
kernel.log("PANIC: "..msg, "PANIC")
|
||||||
|
pcall(kernel["saveLog"])
|
||||||
|
screen:setTextColor(2)
|
||||||
|
screen:setBackgroundColor(0)
|
||||||
|
screen:clear()
|
||||||
|
screen:setCursorPos(1,1)
|
||||||
|
screen:print(LOG_Text)
|
||||||
|
screen:print("KERNEL PANIC!\n"..msg.."\nSystem halted.")
|
||||||
|
screen:print("Press any key to continue...")
|
||||||
|
while true do
|
||||||
|
local event={computer:getMachineEvent()}
|
||||||
|
if event[1]=="keyPressed" then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
computer:reboot()
|
||||||
|
end
|
||||||
|
kernel.panic=kernel.PANIC
|
||||||
|
|
||||||
|
if windowsExp then
|
||||||
|
screen:setTextColor(1)
|
||||||
|
screen:setBackgroundColor(4)
|
||||||
|
screen:clear()
|
||||||
|
local w,h = screen:getSize()
|
||||||
|
screen:setCursorPos(3,5)
|
||||||
|
screen:print(":(")
|
||||||
|
screen:setCursorPos(3,7)
|
||||||
|
screen:print("Your PC ran into a problem and needs to restart. We're just collecting some error")
|
||||||
|
screen:setCursorPos(3,8)
|
||||||
|
screen:print("info, and then we'll restart for you.\n")
|
||||||
|
screen:setCursorPos(3,h-5)
|
||||||
|
screen:print("Stop code: average windows experience")
|
||||||
|
screen:setCursorPos(1,h)
|
||||||
|
screen:print("Press any key to continue... jk reboot it yourself lazy")
|
||||||
|
while true do end
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.log("Kernel loaded.", "INFO")
|
||||||
|
kernel.log("Mounting disks...", "INFO")
|
||||||
|
disks.refresh()
|
||||||
|
ifs.update(disks)
|
||||||
|
ifs.mount("$", "/")
|
||||||
|
|
||||||
|
function kernel.saveLog()
|
||||||
|
ifs.writeAllText("/var/log/syslog.log", LOG_Text)
|
||||||
|
end
|
||||||
|
|
||||||
|
local fstab=ifs.readAllText("/etc/fstab")
|
||||||
|
local split = function(str, delim, maxResultCountOrNil)
|
||||||
|
assert(#delim == 1, "only delim len 1 supported for now")
|
||||||
|
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
|
||||||
|
local rv = {}
|
||||||
|
local buf = ""
|
||||||
|
for i = 1, #str do
|
||||||
|
local c = string.sub(str,i,i)
|
||||||
|
if #rv ~= maxResultCountOrNil and c == delim then
|
||||||
|
table.insert(rv, buf)
|
||||||
|
buf = ""
|
||||||
|
else
|
||||||
|
buf = buf..c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(rv, buf)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(split(fstab,"\n")) do
|
||||||
|
if v:sub(1,1)=="U" then
|
||||||
|
local id=""
|
||||||
|
for i=3,#v do
|
||||||
|
if v:sub(i,i)==";" then
|
||||||
|
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
|
||||||
|
id=v:sub(3,i-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local path=v:sub(#id+4)
|
||||||
|
kernel.log("Mounted "..id.." to "..path)
|
||||||
|
ifs.mount(id,path)
|
||||||
|
::endline::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ifs.remove("/tmp")
|
||||||
|
ifs.makeDir("/tmp")
|
||||||
|
|
||||||
|
local drivers={}
|
||||||
|
drivers.raw={}
|
||||||
|
drivers.processes={}
|
||||||
|
drivers.prior={}
|
||||||
|
drivers.type={}
|
||||||
|
for i=0, 99 do
|
||||||
|
drivers.prior[i]={}
|
||||||
|
end
|
||||||
|
|
||||||
|
function drivers.register(prior,object)
|
||||||
|
drivers.raw[#drivers.raw+1]=object
|
||||||
|
if object.main then drivers.processes[#drivers.processes+1]=object.main end
|
||||||
|
if drivers.prior[prior]==nil then drivers.prior[prior]={} end
|
||||||
|
drivers.prior[prior][#drivers.prior[prior]+1]=object
|
||||||
|
if drivers.type[object.type]==nil then drivers.type[object.type]={} end
|
||||||
|
drivers.type[object.type][#drivers.type[object.type]+1]=object
|
||||||
|
end
|
||||||
|
|
||||||
|
local modules={}
|
||||||
|
for i=0, 99 do
|
||||||
|
modules[i]={}
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,v in ipairs(ifs.list("/lib/modules/Hyperion/")) do
|
||||||
|
local prior=tonumber(v:sub(1,2))
|
||||||
|
modules[prior][#modules[prior]+1]="/lib/modules/Hyperion/"..v
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.drivers=drivers
|
||||||
|
kernel.ifs=ifs
|
||||||
|
kernel.apis=apis
|
||||||
|
kernel.computer=computer
|
||||||
|
kernel.initPath=initPath
|
||||||
|
kernel.arch=arch
|
||||||
|
kernel.initdisks=initdisks
|
||||||
|
kernel.screen=screen
|
||||||
|
for _,p in ipairs(modules) do
|
||||||
|
for _,v in ipairs(p) do
|
||||||
|
local code=ifs.readAllText(v)
|
||||||
|
local func,err=load(code,"@"..v)
|
||||||
|
if not func then kernel.log("ModuLoadErr: "..tostring(err)) goto skip end
|
||||||
|
local status, err = xpcall(func,debug.traceback,kernel)
|
||||||
|
if not status then goto skip end
|
||||||
|
if not err then goto skip end
|
||||||
|
if not err.init then goto skip end
|
||||||
|
local ok, err = xpcall(status.main,debug.traceback)
|
||||||
|
if not ok then kernel.log("ModuInitErr: "..tostring(err)) end
|
||||||
|
::skip::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
kernel.PANIC("Execution complete")
|
||||||
137
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/00_stdlib.kmod
Executable file
137
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/00_stdlib.kmod
Executable file
@@ -0,0 +1,137 @@
|
|||||||
|
function string.hasSuffix(str, suffix)
|
||||||
|
return string.sub(str, #suffix+1) == suffix
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.hasPrefix(str, prefix)
|
||||||
|
return string.sub(str, 1, #prefix) == prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.getSuffix(str, prefix)
|
||||||
|
return string.sub(str, #prefix+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.getPrefix(str, suffix)
|
||||||
|
return string.sub(str, 1, #suffix)
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.join(str, ...)
|
||||||
|
return table.concat(table.pack(str, ...))
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.delim(str, ...)
|
||||||
|
return table.concat(table.pack(...), str)
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.split(str, delim, maxResultCountOrNil)
|
||||||
|
assert(#delim == 1, "only delim len 1 supported for now")
|
||||||
|
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
|
||||||
|
local rv = {}
|
||||||
|
local buf = ""
|
||||||
|
for i = 1, #str do
|
||||||
|
local c = string.sub(str,i,i)
|
||||||
|
if #rv ~= maxResultCountOrNil and c == delim then
|
||||||
|
table.insert(rv, buf)
|
||||||
|
buf = ""
|
||||||
|
else
|
||||||
|
buf = buf..c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(rv, buf)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
function string.replace(str, search, replacement)
|
||||||
|
local rv = ""
|
||||||
|
local consumedLen = 1
|
||||||
|
local i = 1
|
||||||
|
while i<#str do
|
||||||
|
if string.sub(str, i, i+#search-1) == search then
|
||||||
|
rv = rv .. string.sub(str, consumedLen, i-1) .. replacement
|
||||||
|
i=i+#search
|
||||||
|
consumedLen = i
|
||||||
|
end
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
return rv .. string.sub(str, consumedLen)
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.deepcopy(orig, copies)
|
||||||
|
copies = copies or {}
|
||||||
|
|
||||||
|
if type(orig) ~= 'table' then
|
||||||
|
return orig
|
||||||
|
elseif copies[orig] then
|
||||||
|
return copies[orig]
|
||||||
|
end
|
||||||
|
|
||||||
|
local copy = {}
|
||||||
|
copies[orig] = copy
|
||||||
|
|
||||||
|
for k, v in next, orig, nil do
|
||||||
|
local copied_key = table.deepcopy(k, copies)
|
||||||
|
local copied_val = table.deepcopy(v, copies)
|
||||||
|
copy[copied_key] = copied_val
|
||||||
|
end
|
||||||
|
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.hasKey(tabl, query)
|
||||||
|
for i,v in pairs(tabl) do
|
||||||
|
if i==query then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.hasVal(tabl, query)
|
||||||
|
for i,v in pairs(tabl) do
|
||||||
|
if v==query then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function serialize(table)
|
||||||
|
local output = "{"
|
||||||
|
for i,v in pairs(table) do
|
||||||
|
local coma=true
|
||||||
|
if type(i) == "string" then
|
||||||
|
output=output.."[\""..i.."\"]="
|
||||||
|
end
|
||||||
|
if type(v) == "table" then
|
||||||
|
if v == table then
|
||||||
|
output=string.sub(output,1,#output-(#i+1))
|
||||||
|
coma=false
|
||||||
|
else
|
||||||
|
output=output..serialize(v)
|
||||||
|
end
|
||||||
|
elseif type(v) == "string" then
|
||||||
|
output=output.."[=["..v.."]=]"
|
||||||
|
elseif type(v) == "number" then
|
||||||
|
output=output..tostring(v)
|
||||||
|
elseif type(v) == "boolean" then
|
||||||
|
if v == true then
|
||||||
|
output=output.."true"
|
||||||
|
else
|
||||||
|
output=output.."false"
|
||||||
|
end
|
||||||
|
elseif type(v) == "function" then
|
||||||
|
output=output.."function() end"
|
||||||
|
else
|
||||||
|
error("serialization of type \""..type(v).."\" is not supported")
|
||||||
|
end
|
||||||
|
if coma then
|
||||||
|
output=output..","
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #table>0 or string.sub(output,#output,#output) == "," then
|
||||||
|
output=string.sub(output,1,#output-1)
|
||||||
|
end
|
||||||
|
output=output.."}"
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
table.serialize=serialize
|
||||||
217
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/10_vfs.kmod
Executable file
217
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/10_vfs.kmod
Executable file
@@ -0,0 +1,217 @@
|
|||||||
|
local args = {...}
|
||||||
|
local kernel = args[1]
|
||||||
|
local fs = {}
|
||||||
|
local disks = {}
|
||||||
|
local mounts = {["/"]="$"}
|
||||||
|
|
||||||
|
-- path normaliing
|
||||||
|
local function normalizePath(path)
|
||||||
|
if not path or path == "" then return "/" end
|
||||||
|
if path:sub(1,1) ~= "/" then path = "/" .. path end
|
||||||
|
local parts = {}
|
||||||
|
for part in path:gmatch("[^/]+") do
|
||||||
|
if part == ".." then
|
||||||
|
if #parts > 0 then table.remove(parts) end
|
||||||
|
elseif part ~= "." and part ~= "" then
|
||||||
|
parts[#parts+1] = part
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return "/" .. table.concat(parts, "/")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- disk resolution
|
||||||
|
local function resolvePath(path)
|
||||||
|
local abs = normalizePath(path)
|
||||||
|
local best = "/"
|
||||||
|
for mount, diskAddr in pairs(mounts) do
|
||||||
|
if abs:sub(1, #mount) == mount and #mount > #best then
|
||||||
|
best = mount
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local newPath = abs:sub(#best + 1)
|
||||||
|
if newPath == "" then newPath = "/" end
|
||||||
|
return disks[mounts[best]], newPath
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Symlinks
|
||||||
|
-- Format: #!@SYMLINK[target]
|
||||||
|
local SYMLINK_PREFIX = "#!@SYMLINK["
|
||||||
|
local SYMLINK_SUFFIX = "]"
|
||||||
|
local SYMLINK_MAX_DEPTH = 64
|
||||||
|
|
||||||
|
local function isSymlink(disk, path)
|
||||||
|
if not disk:fileExists(path) then return false end
|
||||||
|
local text = disk:readAllText(path)
|
||||||
|
if not text then return false end
|
||||||
|
return text:sub(1, #SYMLINK_PREFIX) == SYMLINK_PREFIX
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readSymlink(disk, path)
|
||||||
|
local text = disk:readAllText(path)
|
||||||
|
if not text then return nil end
|
||||||
|
if text:sub(1, #SYMLINK_PREFIX) ~= SYMLINK_PREFIX then return nil end
|
||||||
|
local target = text:sub(#SYMLINK_PREFIX + 1)
|
||||||
|
if target:sub(-1) == SYMLINK_SUFFIX then
|
||||||
|
target = target:sub(1, -2)
|
||||||
|
end
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Path resolution
|
||||||
|
local function splitPath(p)
|
||||||
|
local parts = {}
|
||||||
|
for part in p:gmatch("[^/]+") do parts[#parts+1] = part end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
|
||||||
|
local function resolveSymlink(path)
|
||||||
|
path = normalizePath(path)
|
||||||
|
local symlinkDepth = 0
|
||||||
|
local parts = splitPath(path)
|
||||||
|
local resolved = {}
|
||||||
|
local i = 1
|
||||||
|
|
||||||
|
while i <= #parts do
|
||||||
|
local comp = parts[i]
|
||||||
|
if comp == "." then
|
||||||
|
elseif comp == ".." then
|
||||||
|
if #resolved > 0 then table.remove(resolved) end
|
||||||
|
else
|
||||||
|
local currentPath = "/" .. table.concat(resolved, "/")
|
||||||
|
local disk, p = resolvePath(currentPath .. "/" .. comp)
|
||||||
|
if isSymlink(disk, p) then
|
||||||
|
symlinkDepth = symlinkDepth + 1
|
||||||
|
if symlinkDepth > SYMLINK_MAX_DEPTH then
|
||||||
|
error("Too many levels of symbolic links: " .. path)
|
||||||
|
end
|
||||||
|
local target = readSymlink(disk, p)
|
||||||
|
if not target then
|
||||||
|
resolved[#resolved+1] = comp
|
||||||
|
else
|
||||||
|
if target:sub(1,1) == "/" then
|
||||||
|
resolved = splitPath(normalizePath(target))
|
||||||
|
else
|
||||||
|
local base = resolved
|
||||||
|
resolved = {}
|
||||||
|
for _, seg in ipairs(base) do resolved[#resolved+1]=seg end
|
||||||
|
for seg in target:gmatch("[^/]+") do
|
||||||
|
if seg == ".." then
|
||||||
|
if #resolved>0 then table.remove(resolved) end
|
||||||
|
elseif seg ~= "." then
|
||||||
|
resolved[#resolved+1]=seg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local remaining = {}
|
||||||
|
for j=i+1,#parts do remaining[#remaining+1]=parts[j] end
|
||||||
|
parts = remaining
|
||||||
|
i = 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
resolved[#resolved+1]=comp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
i=i+1
|
||||||
|
end
|
||||||
|
|
||||||
|
local finalPath="/"..table.concat(resolved,"/")
|
||||||
|
local disk,diskPath=resolvePath(finalPath)
|
||||||
|
return disk,diskPath
|
||||||
|
end
|
||||||
|
|
||||||
|
-- PUBLIC API: MOUNTING
|
||||||
|
function fs.mount(disk, mountPoint)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
mounts[mountPoint]=disk
|
||||||
|
end
|
||||||
|
function fs.unmount(mountPoint)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
mounts[mountPoint]=nil
|
||||||
|
end
|
||||||
|
function fs.virtDisk(diskObj)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
if disks[diskObj.address] then error("Disk exists") end
|
||||||
|
disks[diskObj.address]=diskObj
|
||||||
|
end
|
||||||
|
function fs.eject(addr)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
disks[addr]=nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- SYMLINK API
|
||||||
|
function fs.symlink(target, linkPath)
|
||||||
|
local disk, p = resolvePath(linkPath)
|
||||||
|
return disk:writeAllText(p, SYMLINK_PREFIX..target..SYMLINK_SUFFIX)
|
||||||
|
end
|
||||||
|
function fs.isLink(path)
|
||||||
|
local disk, p = resolvePath(path)
|
||||||
|
return isSymlink(disk,p)
|
||||||
|
end
|
||||||
|
function fs.readLink(path)
|
||||||
|
local disk, p = resolvePath(path)
|
||||||
|
return readSymlink(disk,p)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- FILE OPERATIONS
|
||||||
|
function fs.exists(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:fileExists(p) or disk:directoryExists(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.isFile(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
if isSymlink(disk, p) then return false end
|
||||||
|
return disk:fileExists(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.isDir(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
if isSymlink(disk, p) then return false end
|
||||||
|
return disk:directoryExists(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.list(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:list(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.makeDir(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:makeDirectory(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.remove(path)
|
||||||
|
local disk, p = resolvePath(path)
|
||||||
|
if isSymlink(disk, p) then
|
||||||
|
return disk:remove(p)
|
||||||
|
end
|
||||||
|
local d2, p2 = resolveSymlink(path)
|
||||||
|
return d2:remove(p2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.readAllText(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:readAllText(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.writeAllText(path, text)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:writeAllText(p, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.appendAllText(path, text)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:appendAllText(p, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fs.getSize(path)
|
||||||
|
local disk, p = resolveSymlink(path)
|
||||||
|
return disk:getSize(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- INIT
|
||||||
|
for _,v in kernel.initdisks.list() do fs.virtDisk(v) end
|
||||||
|
kernel.fs=fs
|
||||||
|
kernel.chache.preload.fs=fs
|
||||||
|
kernel.chache.preload.filesystem=fs
|
||||||
22
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/11_fstabInit.kmod
Executable file
22
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/11_fstabInit.kmod
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
local ifs=kernel.ifs
|
||||||
|
local initdisks=kernel.initdisks
|
||||||
|
|
||||||
|
local fstab=ifs.readAllText("/etc/fstab")
|
||||||
|
kernel.fs.update(initdisks)
|
||||||
|
for i,v in ipairs(string.split(fstab,"\n")) do
|
||||||
|
if v:sub(1,1)=="U" then
|
||||||
|
local id=""
|
||||||
|
for i=3,#v do
|
||||||
|
if v:sub(i,i)==";" then
|
||||||
|
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
|
||||||
|
id=v:sub(3,i-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local path=v:sub(#id+4)
|
||||||
|
kernel.log("Mounted "..id.." to "..path)
|
||||||
|
kernel.fs.mount(id,path)
|
||||||
|
::endline::
|
||||||
|
end
|
||||||
|
end
|
||||||
182
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/11_mkvirtfs.kmod
Executable file
182
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/11_mkvirtfs.kmod
Executable file
@@ -0,0 +1,182 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
|
||||||
|
function kernel.fs.mkvirtfs(address, readOnly, label, autoRegister)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
local disk = {}
|
||||||
|
disk.address = address
|
||||||
|
local isRO = readOnly or false
|
||||||
|
|
||||||
|
local backend = {
|
||||||
|
["/"] = { __dir = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
-- Helpers
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
local function norm(path)
|
||||||
|
if not path or path == "" then return "/" end
|
||||||
|
if path:sub(1,1) ~= "/" then path = "/" .. path end
|
||||||
|
path = path:gsub("/+", "/")
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
local function split(path)
|
||||||
|
local parts = {}
|
||||||
|
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getNode(path)
|
||||||
|
path = norm(path)
|
||||||
|
if path == "/" then return backend["/"] end
|
||||||
|
local node = backend["/"]
|
||||||
|
for _, part in ipairs(split(path)) do
|
||||||
|
node = node[part]
|
||||||
|
if not node then return nil end
|
||||||
|
end
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getParent(path)
|
||||||
|
local parts = split(norm(path))
|
||||||
|
local name = table.remove(parts)
|
||||||
|
local parentPath = "/" .. table.concat(parts, "/")
|
||||||
|
return getNode(parentPath), name
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ensureWrite()
|
||||||
|
if isRO then return false, "read-only" end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
-- File IO (supports string or file-object with read/write funcs)
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
local function fileRead(node)
|
||||||
|
if type(node) == "string" then
|
||||||
|
return node
|
||||||
|
elseif type(node) == "table" and node.__file and node.read then
|
||||||
|
return node.read()
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fileWrite(parent, name, text)
|
||||||
|
local node = parent[name]
|
||||||
|
if type(node) == "string" then
|
||||||
|
parent[name] = text
|
||||||
|
return true
|
||||||
|
elseif type(node) == "table" and node.__file and node.write then
|
||||||
|
return node.write(text)
|
||||||
|
end
|
||||||
|
parent[name] = text
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fileAppend(parent, name, text)
|
||||||
|
local node = parent[name]
|
||||||
|
if type(node) == "string" then
|
||||||
|
parent[name] = node .. text
|
||||||
|
return true
|
||||||
|
elseif type(node) == "table" and node.__file and node.write then
|
||||||
|
return node.write((node.read() or "") .. text)
|
||||||
|
end
|
||||||
|
parent[name] = text
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
-- API
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
function disk:isReadOnly() return isRO end
|
||||||
|
function disk:spaceUsed() return 0 end
|
||||||
|
function disk:spaceTotal() return 0 end
|
||||||
|
|
||||||
|
function disk:readAllText(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
if not node then return nil, "file not found" end
|
||||||
|
return fileRead(node)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:writeAllText(path, text)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
|
||||||
|
local parent, name = getParent(path)
|
||||||
|
if not parent or not parent.__dir then return false, "invalid directory" end
|
||||||
|
return fileWrite(parent, name, tostring(text))
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:appendAllText(path, text)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
|
||||||
|
local parent, name = getParent(path)
|
||||||
|
if not parent or not parent.__dir then return false, "invalid directory" end
|
||||||
|
return fileAppend(parent, name, tostring(text))
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:list(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
if not node or not node.__dir then return nil, "not a directory" end
|
||||||
|
local out = {}
|
||||||
|
for k, _ in pairs(node) do if k ~= "__dir" then table.insert(out, k) end end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:fileExists(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
return type(node) == "string" or (type(node) == "table" and node.__file)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:directoryExists(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
return node and type(node) == "table" and node.__dir or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:makeDirectory(path)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
local parent, name = getParent(path)
|
||||||
|
if not parent or not parent.__dir then return false, "invalid parent directory" end
|
||||||
|
parent[name] = { __dir = true }
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:remove(path)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
local parent, name = getParent(path)
|
||||||
|
if not parent or not parent[name] then return false, "not found" end
|
||||||
|
parent[name] = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:setLabel(new)
|
||||||
|
local ok, err = ensureWrite(); if not ok then return false, err end
|
||||||
|
label = tostring(new)
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:getLabel()
|
||||||
|
return label
|
||||||
|
end
|
||||||
|
|
||||||
|
function disk:size(path)
|
||||||
|
local node = getNode(path)
|
||||||
|
if type(node) == "string" then return #node end
|
||||||
|
if type(node) == "table" and node.__file and node.read then
|
||||||
|
local v = node.read()
|
||||||
|
return v and #v or 0
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
-- Auto-register with kernel and return backend
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
if kernel and kernel.fs and kernel.fs.virtDisk and autoRegister then
|
||||||
|
kernel.fs.virtDisk(disk)
|
||||||
|
end
|
||||||
|
|
||||||
|
return backend, disk
|
||||||
|
end
|
||||||
56
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/11_require.kmod
Executable file
56
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/11_require.kmod
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
local args = {...}
|
||||||
|
local kernel = args[1]
|
||||||
|
|
||||||
|
-- List of search paths
|
||||||
|
local paths = {
|
||||||
|
"/lib/?",
|
||||||
|
"/usr/lib/?",
|
||||||
|
"/usr/local/lib/?"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Custom require implementation
|
||||||
|
function require(module)
|
||||||
|
-- Return cached module if it already exists
|
||||||
|
if kernel.cache.preload[module] then
|
||||||
|
return kernel.cache.preload[module]
|
||||||
|
end
|
||||||
|
|
||||||
|
local err = {}
|
||||||
|
|
||||||
|
for _, path in ipairs(paths) do
|
||||||
|
-- Replace "?" with module name
|
||||||
|
local filePath = string.replace(path, "?", module)
|
||||||
|
|
||||||
|
-- Try to open file
|
||||||
|
local file = kernel.fs.isFile(filePath)
|
||||||
|
if file then
|
||||||
|
local content = kernel.fs.readAllText(filePath)
|
||||||
|
|
||||||
|
-- Load the module as Lua code
|
||||||
|
local chunk, loadErr = load(content, filePath)
|
||||||
|
if not chunk then
|
||||||
|
table.insert(err, "Error loading: " .. filePath .. ": " .. loadErr)
|
||||||
|
else
|
||||||
|
-- Execute the module. If the module returns a value, cache it.
|
||||||
|
local ok, result = xpcall(chunk, debug.traceback)
|
||||||
|
if not ok then
|
||||||
|
table.insert(err, "Error executing: "..filePath..": "..tostring(result))
|
||||||
|
else
|
||||||
|
if result ~= nil then
|
||||||
|
kernel.cache.preload[module] = result
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If module doesn't return anything, cache `true` like Lua does
|
||||||
|
kernel.cache.preload[module] = true
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(err, "Module not found: " .. filePath)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If nothing worked, raise an error with all reasons
|
||||||
|
error("Unable to require module '" .. module .. "':\n" .. table.concat(err, "\n"))
|
||||||
|
end
|
||||||
17
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/12_devfs.kmod
Executable file
17
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/12_devfs.kmod
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel = args[1]
|
||||||
|
|
||||||
|
local data = kernel.fs.mkvirtfs("devfs0000", true, "devfs", false)
|
||||||
|
|
||||||
|
data["/"]["OSVERSION"]="Hyperion 1.0.0"
|
||||||
|
data["/"]["eeprom"]={
|
||||||
|
__file=true,
|
||||||
|
read=function()
|
||||||
|
return kernel.computer:getEEPROM()
|
||||||
|
end,
|
||||||
|
write=function(text)
|
||||||
|
if kernel.uid ~= 0 then error("Permission Denied") end
|
||||||
|
kernel.computer:setEEPROM(text)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
12
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/20_keventd.kmod
Executable file
12
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/20_keventd.kmod
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
kernel.drivers.processes[#kernel.drivers.processes+1]=function()
|
||||||
|
while true do
|
||||||
|
local event={kernel.computer:getMachineEvent()}
|
||||||
|
while event[1]~=nil do
|
||||||
|
kernel.hpv.sendSig("all", table.unpack(event))
|
||||||
|
event={kernel.computer:getMachineEvent()}
|
||||||
|
end
|
||||||
|
coroutine.yield()
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[2]
|
||||||
|
|
||||||
|
for _,subf in ipairs(kernel.fs.list("/lib/modules/")) do
|
||||||
|
if subf~="Hyperion" then
|
||||||
|
for _,driver in ipairs(kernel.fs.list("/lib/modules/"..subf)) do
|
||||||
|
local code = kernel.fs.readAllText("/lib/modules/"..subf.."/"..driver)
|
||||||
|
local func, err = load(code, "@"..driver)
|
||||||
|
if not func then
|
||||||
|
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
|
||||||
|
else
|
||||||
|
local ok, err = xpcall(func, debug.traceback)
|
||||||
|
if not ok then
|
||||||
|
kernel.log("DriverLoadErr: "..tostring(err), "ERROR")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
11
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/31_thirdPartyInit.kmod
Executable file
11
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/31_thirdPartyInit.kmod
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel=args[1]
|
||||||
|
|
||||||
|
for _,l in ipairs(kernel.drivers.prior) do
|
||||||
|
for _,d in ipairs(l) do
|
||||||
|
if d.init then
|
||||||
|
local ok,err = xpcall(d.init, debug.traceback)
|
||||||
|
if not ok then kernel.log("DriverInitErr: "..tostring(err)) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
0
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/98_dbgProtect.kmod
Executable file
0
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/98_dbgProtect.kmod
Executable file
167
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/99_init.kmod
Executable file
167
Test/Hyperion-kernel-v0.1.0/lib/modules/Hyperion/99_init.kmod
Executable file
@@ -0,0 +1,167 @@
|
|||||||
|
local args={...}
|
||||||
|
local kernel = args[1]
|
||||||
|
local tasks={}
|
||||||
|
local currentTask={}
|
||||||
|
local signals={}
|
||||||
|
local tid=1
|
||||||
|
local gid=1
|
||||||
|
local sys={}
|
||||||
|
|
||||||
|
function sys.hookSig(sig, func)
|
||||||
|
if not signals[tostring(currentTask.pid)][sig] then
|
||||||
|
signals[tostring(currentTask.pid)][sig]={}
|
||||||
|
end
|
||||||
|
signals[tostring(currentTask.pid)][sig][#signals[tostring(currentTask.pid)][sig]+1]=func
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.clearSigHooks(typ)
|
||||||
|
if not typ or typ == "all" then
|
||||||
|
signals[tostring(currentTask.pid)]={}
|
||||||
|
currentTask.signal=signals[tostring(currentTask.pid)]
|
||||||
|
else
|
||||||
|
if currentTask.signal[typ] then
|
||||||
|
currentTask.signal[typ]={}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.sendSig(pid, signal, ...)
|
||||||
|
if pid=="all" then
|
||||||
|
for i,v in pairs(tasks) do
|
||||||
|
v.sigQ[#v.sigQ+1]={signal, ...}
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not tasks[tostring(pid)] then return false end
|
||||||
|
tasks[tostring(pid)].sigQ[#tasks[tostring(pid)].sigQ+1]={signal, ...}
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.flushSigs()
|
||||||
|
local ret = {}
|
||||||
|
for i=1, #currentTask.sigQ do
|
||||||
|
if currentTask.signal[currentTask.sigQ[i][1]] then
|
||||||
|
for _,v in ipairs(currentTask.signal[currentTask.sigQ[1][1]]) do
|
||||||
|
coroutine.resumeWithTimeout(coroutine.create(function()
|
||||||
|
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
|
||||||
|
if not ok then
|
||||||
|
table.insert(ret, err)
|
||||||
|
end
|
||||||
|
end), 10)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for _,v in ipairs(currentTask.signal["unhandledEvent"]) do
|
||||||
|
coroutine.resumeWithTimeout(coroutine.create(function()
|
||||||
|
local ok, err = xpcall(v, debug.traceback, table.unpack(table.remove(currentTask.sigQ, 1)))
|
||||||
|
if not ok then
|
||||||
|
table.insert(ret, err)
|
||||||
|
end
|
||||||
|
end), 10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function sys.spawn(func, name, evars, args)
|
||||||
|
local id=tid
|
||||||
|
tid=tid+1
|
||||||
|
name=name or tostring(id)
|
||||||
|
|
||||||
|
signals[tostring(id)]={}
|
||||||
|
tasks[tostring(id)]={
|
||||||
|
coro=coroutine.create(function()
|
||||||
|
local ret = {xpcall(func, debug.traceback, table.unpack(args))}
|
||||||
|
if not ret[1] then
|
||||||
|
sys.sendSig(currentTask.ppid, "ChildTaskError", id, err)
|
||||||
|
else
|
||||||
|
sys.sendSig(currentTask.ppid, "ChildTaskExit", id, table.unpack(ret, 2))
|
||||||
|
end
|
||||||
|
currentTask.status="Z"
|
||||||
|
end),
|
||||||
|
name=name,
|
||||||
|
pid=id,
|
||||||
|
ppid=currentTask.pid,
|
||||||
|
tgid=currentTask.tgid,
|
||||||
|
user=kernel.user,
|
||||||
|
uid=kernel.uid,
|
||||||
|
evars=evars,
|
||||||
|
args=args,
|
||||||
|
vy=0,
|
||||||
|
ivy=0,
|
||||||
|
status="R",
|
||||||
|
signal=signals[tostring(id)],
|
||||||
|
parent=currentTask,
|
||||||
|
children={},
|
||||||
|
sibling=currentTask.children,
|
||||||
|
sigQ={}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function collectZombieProc()
|
||||||
|
local ret = {}
|
||||||
|
for _,v in pairs(tasks) do
|
||||||
|
if v.status=="Z" then
|
||||||
|
local pid = v.pid
|
||||||
|
for _,c in ipairs(v.children) do
|
||||||
|
c.parent=tasks["1"]
|
||||||
|
c.sibling=tasks["1"].children
|
||||||
|
c.ppid=1
|
||||||
|
c.tgid=1
|
||||||
|
end
|
||||||
|
signals[tostring(pid)]=nil
|
||||||
|
tasks[tostring(pid)]=nil
|
||||||
|
table.insert(ret, pid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
signals["1"]={}
|
||||||
|
tasks["1"]={
|
||||||
|
coro=coroutine.create(function()
|
||||||
|
local ret = {xpcall(kernel.fs.load(kernel.initPath), debug.traceback)}
|
||||||
|
if not ok then
|
||||||
|
kernel.panic(err)
|
||||||
|
else
|
||||||
|
kernel.panic("Attempted to kill init!")
|
||||||
|
end
|
||||||
|
end),
|
||||||
|
name="SysInit",
|
||||||
|
pid=1,
|
||||||
|
ppid=0,
|
||||||
|
tgid=1,
|
||||||
|
user=kernel.user,
|
||||||
|
uid=kernel.uid,
|
||||||
|
evars={},
|
||||||
|
args={kernel},
|
||||||
|
vy=0,
|
||||||
|
ivy=0,
|
||||||
|
status="R",
|
||||||
|
signal=signals["1"],
|
||||||
|
parent={name="Hyprkrnl",pid=0},
|
||||||
|
children={},
|
||||||
|
sibling={},
|
||||||
|
sigQ={}
|
||||||
|
}
|
||||||
|
kernel.chache.preload.sys=sys
|
||||||
|
kernel.chache.preload.system=sys
|
||||||
|
kernel.hpv=sys
|
||||||
|
|
||||||
|
kernel.saveLog()
|
||||||
|
while true do
|
||||||
|
for _,v in pairs(tasks) do
|
||||||
|
currentTask=v
|
||||||
|
kernel.user=currentTask.user
|
||||||
|
kernel.uid=currentTask.uid
|
||||||
|
sys.flushSigs()
|
||||||
|
local status = coroutine.resumeWithTimeout(currentTask.coro, 50)
|
||||||
|
if status then
|
||||||
|
currentTask.vy=currentTask.vy+1
|
||||||
|
else
|
||||||
|
currentTask.ivy=currentTask.ivy+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
collectZombieProc()
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel.panic("Exited pid 0")
|
||||||
14
Test/HyperionOS.pkg
Executable file
14
Test/HyperionOS.pkg
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Name": "HyperionOS",
|
||||||
|
"Version": "0.1.0",
|
||||||
|
"Publishers": ["HyperionOS Dev Team"],
|
||||||
|
"Description": "A sample package for HyperionOS project.",
|
||||||
|
"Dependencies": [
|
||||||
|
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-core-v0.1.0.pkg",
|
||||||
|
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-kernel-v0.1.0.pkg",
|
||||||
|
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-ui-v0.1.0.pkg",
|
||||||
|
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-network-v0.1.0.pkg",
|
||||||
|
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-security-v0.1.0.pkg",
|
||||||
|
"https://git.astronand.dev/Hyperion/HyperionOS/archive/Hyperion-filesystem-v0.1.0.pkg"
|
||||||
|
]
|
||||||
|
}
|
||||||
16
Test/palette_16.txt
Executable file
16
Test/palette_16.txt
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
0: #000000
|
||||||
|
1: #FFFFFF
|
||||||
|
2: #FF0000
|
||||||
|
3: #00FF00
|
||||||
|
4: #0000FF
|
||||||
|
5: #00FFFF
|
||||||
|
6: #FF00FF
|
||||||
|
7: #FFFF00
|
||||||
|
8: #FF6D00
|
||||||
|
9: #6DFF55
|
||||||
|
10: #24FFFF
|
||||||
|
11: #924900
|
||||||
|
12: #6D6D55
|
||||||
|
13: #DBDBAA
|
||||||
|
14: #6D00FF
|
||||||
|
15: #B6FF00
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
readAllText(dir) Returns string:contents
|
|
||||||
reads all text from directory on disk
|
|
||||||
|
|
||||||
writeAllText(dir, content) Returns nil
|
|
||||||
writes all content to file
|
|
||||||
|
|
||||||
appendAllText(dir, content) Returns nil
|
|
||||||
appends all content to file
|
|
||||||
|
|
||||||
getAtributes(dir) Returns table:atributes
|
|
||||||
returns atributes of a file or directory
|
|
||||||
|
|
||||||
list(dir) Returns table:files
|
|
||||||
returns contents of directory
|
|
||||||
|
|
||||||
mkdir(dir) Returns nil
|
|
||||||
makes specified directory
|
|
||||||
|
|
||||||
mkfile(dir) Returns nil
|
|
||||||
makes specified file
|
|
||||||
|
|
||||||
-------------------------------------------------------
|
|
||||||
Atributes table
|
|
||||||
number:size
|
|
||||||
size of file in bytes
|
|
||||||
|
|
||||||
number:owner
|
|
||||||
owner UUID
|
|
||||||
|
|
||||||
number:group
|
|
||||||
group UUID
|
|
||||||
|
|
||||||
number:perms
|
|
||||||
file perms 0-21
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# drivers
|
|
||||||
---
|
|
||||||
|
|
||||||
## driver types
|
|
||||||
---
|
|
||||||
|
|
||||||
```
|
|
||||||
http - internet
|
|
||||||
lan - local networks
|
|
||||||
fs - filesystems
|
|
||||||
disk - normal disks
|
|
||||||
udisk - byte array disks
|
|
||||||
terminal - screens that only support text
|
|
||||||
```
|
|
||||||
|
|
||||||
### Driver APIS
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
print(text) Returns nil
|
|
||||||
Prints text to the terminal with a trailing \n
|
|
||||||
|
|
||||||
printInline(text) Returns nil
|
|
||||||
Same as print without the trailing \n
|
|
||||||
|
|
||||||
clear() Returns nil
|
|
||||||
Clears screen
|
|
||||||
26
docs/kernel/drivers/api/README.md
Executable file
26
docs/kernel/drivers/api/README.md
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
# Drivers
|
||||||
|
---
|
||||||
|
|
||||||
|
Hyperion OS supports many driver types to allow it to run on any hardware
|
||||||
|
```
|
||||||
|
Driver types
|
||||||
|
tto - Supports basic text terminal output
|
||||||
|
gpio - Supports things like redstone
|
||||||
|
runner - Kernel level programs (no api)
|
||||||
|
timer - Timers and time related
|
||||||
|
periph - Basic peripheral info
|
||||||
|
gfx - PixelScreens
|
||||||
|
```
|
||||||
|
Hyperion also has a base driver api
|
||||||
|
```
|
||||||
|
Driver API
|
||||||
|
name - Name of driver+
|
||||||
|
type - Type of driver
|
||||||
|
init - Ran before init
|
||||||
|
main - Ran as a process and has normal behavior (used for checking network like things)
|
||||||
|
api - api difined by type
|
||||||
|
arch - architecture difined in bootloader (EX: cct, oc, ac, cc, ccpc)
|
||||||
|
description - discription
|
||||||
|
author - author of driver
|
||||||
|
prior - priority (low first)
|
||||||
|
```
|
||||||
35
docs/kernel/drivers/api/tto.md
Executable file
35
docs/kernel/drivers/api/tto.md
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
# tty driver
|
||||||
|
---
|
||||||
|
|
||||||
|
tty (Teletypewriter) is a driver class made for basic text output (ASCII only) used for primitive ui.
|
||||||
|
|
||||||
|
```
|
||||||
|
API Signature
|
||||||
|
|
||||||
|
print(String: text):Nil
|
||||||
|
Prints text to the screen with a following \n
|
||||||
|
|
||||||
|
printInline(String: text):Nil
|
||||||
|
Prints text to the screen without following \n
|
||||||
|
|
||||||
|
clear():Nil
|
||||||
|
Clears screen sets x,y of cursor to 0,0
|
||||||
|
|
||||||
|
setBackgroundColor(Number: index):Nil
|
||||||
|
Sets background color to index of pallete
|
||||||
|
|
||||||
|
getBackgroundColor():Number
|
||||||
|
Returns current background index of screen
|
||||||
|
|
||||||
|
setForegroundColor(Number: index):Nil
|
||||||
|
Sets foreground color to index of pallete
|
||||||
|
|
||||||
|
getForegroundColor():Number
|
||||||
|
Returns current foreground index of screen
|
||||||
|
|
||||||
|
setCursorPos(Number: x, Number: y):Nil
|
||||||
|
Sets x,y position of cursor
|
||||||
|
|
||||||
|
getCursorPos():Number, Number
|
||||||
|
Gets x,y position of cursor
|
||||||
|
```
|
||||||
1
tools/boot.lua
Executable file
1
tools/boot.lua
Executable file
@@ -0,0 +1 @@
|
|||||||
|
dofile("/$/boot/cct/eeprom")
|
||||||
47
tools/listtar.lua
Executable file
47
tools/listtar.lua
Executable file
@@ -0,0 +1,47 @@
|
|||||||
|
-- tar_list.lua
|
||||||
|
-- List all paths stored in a TAR file (no extraction)
|
||||||
|
|
||||||
|
local function octal_to_number(str)
|
||||||
|
str = str:gsub("%z", ""):match("^%s*(.-)%s*$")
|
||||||
|
return tonumber(str, 8) or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local file = ({...})[1]
|
||||||
|
if not file then
|
||||||
|
print("Usage: tar_list <tarfile>")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = fs.open(file, "r")
|
||||||
|
local tar = f.readAll()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
local i = 1
|
||||||
|
local len = #tar
|
||||||
|
|
||||||
|
while i + 512 <= len do
|
||||||
|
local header = tar:sub(i, i + 511)
|
||||||
|
|
||||||
|
if header:match("^\0+$") then break end
|
||||||
|
|
||||||
|
-- Extract raw name & prefix
|
||||||
|
local name = header:sub(1, 100):gsub("%z.*","")
|
||||||
|
local prefix = header:sub(346, 500):gsub("%z.*","")
|
||||||
|
|
||||||
|
local size = octal_to_number(header:sub(125, 136))
|
||||||
|
i = i + 512
|
||||||
|
|
||||||
|
-- Skip file contents
|
||||||
|
local pad = (512 - (size % 512)) % 512
|
||||||
|
i = i + size + pad
|
||||||
|
|
||||||
|
local full
|
||||||
|
if prefix ~= "" then
|
||||||
|
full = prefix .. "/" .. name
|
||||||
|
else
|
||||||
|
full = name
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Print exactly what Windows Explorer stored
|
||||||
|
print(full)
|
||||||
|
end
|
||||||
186
tools/unpack.lua
Executable file
186
tools/unpack.lua
Executable file
@@ -0,0 +1,186 @@
|
|||||||
|
-- unpack_tar.lua
|
||||||
|
-- Fully working TAR unpacker for ComputerCraft
|
||||||
|
-- Handles Windows Explorer TAR bug (duplicated path segments)
|
||||||
|
|
||||||
|
local function octal_to_number(str)
|
||||||
|
str = str:gsub("%z", ""):match("^%s*(.-)%s*$")
|
||||||
|
return tonumber(str, 8) or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
-- Fix Windows Explorer TAR bug: repeated prefix segments in name
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
local function dedupe_path(path)
|
||||||
|
-- Example:
|
||||||
|
-- Build/boot/Build/boot/cct/kernel.lua
|
||||||
|
-- -> Build/boot/cct/kernel.lua
|
||||||
|
|
||||||
|
local parts = {}
|
||||||
|
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
|
||||||
|
|
||||||
|
-- Try all possible prefix lengths
|
||||||
|
for prefix_len = 1, math.floor(#parts / 2) do
|
||||||
|
local ok = true
|
||||||
|
for i = 1, prefix_len do
|
||||||
|
if parts[i] ~= parts[i + prefix_len] then
|
||||||
|
ok = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if ok then
|
||||||
|
-- Remove one repeated prefix
|
||||||
|
local cleaned = {}
|
||||||
|
for i = 1, #parts - prefix_len do
|
||||||
|
cleaned[#cleaned + 1] = parts[i + prefix_len]
|
||||||
|
end
|
||||||
|
return table.concat(cleaned, "/")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
-- Directory tree helpers
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function make_dirs(root, path)
|
||||||
|
local cur = root
|
||||||
|
for part in path:gmatch("([^/]+)/") do
|
||||||
|
if not cur[part] then
|
||||||
|
cur[part] = { __type = "dir", __entries = {} }
|
||||||
|
end
|
||||||
|
cur = cur[part].__entries
|
||||||
|
end
|
||||||
|
return cur
|
||||||
|
end
|
||||||
|
|
||||||
|
local function flatten(node, prefix)
|
||||||
|
local out = {}
|
||||||
|
prefix = prefix or ""
|
||||||
|
|
||||||
|
for name, obj in pairs(node) do
|
||||||
|
local full = prefix .. name
|
||||||
|
if obj.__type == "file" then
|
||||||
|
out[#out+1] = {
|
||||||
|
name = full,
|
||||||
|
type = "file",
|
||||||
|
contents = obj.__contents
|
||||||
|
}
|
||||||
|
elseif obj.__type == "dir" then
|
||||||
|
out[#out+1] = {
|
||||||
|
name = full .. "/",
|
||||||
|
type = "dir",
|
||||||
|
contents = flatten(obj.__entries, full .. "/")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
-- TAR Unpacker
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
local function unpack_tar(tarstr)
|
||||||
|
local i = 1
|
||||||
|
local len = #tarstr
|
||||||
|
local root = {}
|
||||||
|
|
||||||
|
while i + 512 <= len do
|
||||||
|
local header = tarstr:sub(i, i + 511)
|
||||||
|
|
||||||
|
-- End of archive (null block)
|
||||||
|
if header:match("^\0+$") then break end
|
||||||
|
|
||||||
|
-- Read name + ustar prefix
|
||||||
|
local name_raw = header:sub(1, 100):gsub("%z.*", "")
|
||||||
|
local prefix_raw = header:sub(346, 500):gsub("%z.*", "")
|
||||||
|
|
||||||
|
local name
|
||||||
|
if prefix_raw ~= "" then
|
||||||
|
name = prefix_raw .. "/" .. name_raw
|
||||||
|
else
|
||||||
|
name = name_raw
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Normalize path
|
||||||
|
name = name:gsub("^%./", ""):gsub("/+", "/")
|
||||||
|
|
||||||
|
-- Fix Windows Explorer TAR bug
|
||||||
|
name = dedupe_path(name)
|
||||||
|
|
||||||
|
local size = octal_to_number(header:sub(125,136))
|
||||||
|
local typeflag = header:sub(157,157)
|
||||||
|
|
||||||
|
i = i + 512
|
||||||
|
|
||||||
|
local contents = tarstr:sub(i, i + size - 1)
|
||||||
|
local pad = (512 - (size % 512)) % 512
|
||||||
|
i = i + size + pad
|
||||||
|
|
||||||
|
if name == "" then goto continue end
|
||||||
|
|
||||||
|
-- Determine if it’s a directory
|
||||||
|
local is_dir = typeflag == "5" or name:sub(-1) == "/"
|
||||||
|
|
||||||
|
-- Remove trailing slash for hierarchical processing
|
||||||
|
local clean_name = name:gsub("/$", "")
|
||||||
|
if clean_name == "" then goto continue end
|
||||||
|
|
||||||
|
local parent_path = clean_name:match("(.+)/")
|
||||||
|
local fname = clean_name:match("([^/]+)$")
|
||||||
|
if not fname then goto continue end
|
||||||
|
|
||||||
|
local parent = root
|
||||||
|
if parent_path then
|
||||||
|
parent = make_dirs(root, parent_path .. "/")
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_dir then
|
||||||
|
parent[fname] = parent[fname] or { __type = "dir", __entries = {} }
|
||||||
|
else
|
||||||
|
parent[fname] = { __type = "file", __contents = contents }
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
return flatten(root)
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
-- FS output (ComputerCraft)
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
local function write_directory(prefix, items)
|
||||||
|
for _, v in ipairs(items) do
|
||||||
|
if v.type == "dir" then
|
||||||
|
fs.makeDir(prefix..v.name)
|
||||||
|
write_directory(prefix, v.contents)
|
||||||
|
elseif v.type == "file" then
|
||||||
|
local file = fs.open(prefix..v.name, "w")
|
||||||
|
file.write(v.contents)
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
-- Entry point
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
local in_tar = ({...})[1]
|
||||||
|
local out_dir = ({...})[2]
|
||||||
|
|
||||||
|
if not in_tar or not out_dir then
|
||||||
|
print("Usage: unpack_tar <tarfile> <output_dir>")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = fs.open(in_tar, "r")
|
||||||
|
local tarstr = f.readAll()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
local list = unpack_tar(tarstr)
|
||||||
|
write_directory(out_dir, list)
|
||||||
|
|
||||||
|
print("TAR extracted into: " .. out_dir)
|
||||||
5
tools/update.lua
Executable file
5
tools/update.lua
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
shell.run("unpack Build.tar /")
|
||||||
|
shell.run("rm $")
|
||||||
|
shell.run("cp Build $")
|
||||||
|
shell.run("rm Build")
|
||||||
|
shell.run("rm Build.tar")
|
||||||
Reference in New Issue
Block a user