From 16c900de848ae221f1fbd2497f4ca55d39ca5152 Mon Sep 17 00:00:00 2001 From: Astronand Date: Mon, 2 Mar 2026 21:23:35 -0500 Subject: [PATCH] fixed ls links, modules writeable --- .vscode/settings.json | 3 +- Src/Hyperion-firmware-ccpc/boot/cct/boot.lua | 316 ++++++++++++++ Src/Hyperion-firmware-ccpc/boot/cct/eeprom | 119 ++++++ Src/Hyperion-firmware-ccpc/boot/cct/initdisks | 155 +++++++ .../lib/modules/CC-Tweaked/25_tty.kmod | 394 ++++++++++++++++++ .../lib/modules/CC-Tweaked/40_http.kmod} | 0 .../lib/modules/CC-Tweaked/40_modem.kmod | 0 .../lib/modules/CC-Tweaked/40_redstone.kmod | 26 ++ .../lib/modules/CC-Tweaked/25_tty.kmod | 16 + Src/Hyperion-kernel/boot/kernel.lua | 18 +- .../lib/modules/hyperion/10_vfs.kmod | 30 +- .../lib/modules/hyperion/12_devfs.kmod | 2 +- .../lib/modules/hyperion/92_permissions.kmod | 118 +----- build.py | 158 ++++--- building.md | 73 ++-- 15 files changed, 1198 insertions(+), 230 deletions(-) create mode 100644 Src/Hyperion-firmware-ccpc/boot/cct/boot.lua create mode 100644 Src/Hyperion-firmware-ccpc/boot/cct/eeprom create mode 100644 Src/Hyperion-firmware-ccpc/boot/cct/initdisks create mode 100644 Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/25_tty.kmod rename Src/{Hyperion-installer/@CD/install => Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_http.kmod} (100%) create mode 100644 Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_modem.kmod create mode 100644 Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_redstone.kmod diff --git a/.vscode/settings.json b/.vscode/settings.json index 81d26b7..138447a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "syscall", "printf", "printInline", - "toHex" + "toHex", + "loadcstr" ] } \ No newline at end of file diff --git a/Src/Hyperion-firmware-ccpc/boot/cct/boot.lua b/Src/Hyperion-firmware-ccpc/boot/cct/boot.lua new file mode 100644 index 0000000..7bdae02 --- /dev/null +++ b/Src/Hyperion-firmware-ccpc/boot/cct/boot.lua @@ -0,0 +1,316 @@ +-- :Minify:-- +local BOOT_DRIVE_PATH = ({...})[1] or "/$" +---@diagnostic disable-next-line: undefined-global +local term = term +local os = os +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 + + if x > w then + x = 1 + y = y + 1 + end + + if y - 1 >= h then + term.scroll(1) + y = h + term.setCursorPos(x, y) + end + end + + term.setCursorPos(x, y) +end + +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 + +term.setCursorBlink(false) +local ok, err = xpcall(function() + local apis = {BOOT_DRIVE_PATH = BOOT_DRIVE_PATH} + + local lua = { + coroutine = true, + debug = 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 + } + + local debug = debug + for i, v in pairs(_G) do + if not lua[i] or lua[i] == nil then + apis[i] = v + _G[i] = nil + end + end + + local acekeys={ + [apis.keys.enter]="\n", + [apis.keys.tab]="\t", + [apis.keys.backspace]="\b", + [apis.keys.up]="\17", + [apis.keys.down]="\18", + [apis.keys.left]="\19", + [apis.keys.right]="\20", + } + + function sleep(time) + local stoptime = apis.os.clock() + (time) + while stoptime > apis.os.clock() do end + end + + apis.term.setPaletteColor(0x1, 0xFFFFFF) -- #000000 + apis.term.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF + apis.term.setPaletteColor(0x4, 0x00FF00) -- #FF0000 + apis.term.setPaletteColor(0x8, 0x0000FF) -- #00FF00 + apis.term.setPaletteColor(0x10, 0x00FFFF) -- #0000FF + apis.term.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF + apis.term.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF + apis.term.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00 + apis.term.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00 + apis.term.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55 + apis.term.setPaletteColor(0x400, 0x924900) -- #24FFFF + apis.term.setPaletteColor(0x800, 0x6D6D55) -- #924900 + apis.term.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55 + apis.term.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA + apis.term.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF + apis.term.setPaletteColor(0x8000, 0x000000) -- #B6FF00 + + 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 + + local Kernel = load(getFile(BOOT_DRIVE_PATH .. "/boot/kernel.lua"),"@Kernel") + local initFs = load(getFile(BOOT_DRIVE_PATH .. "/boot/cct/initdisks"),"@Init_disks")(apis) + local fs = load(getFile(BOOT_DRIVE_PATH .. "/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 + + local eventQueue = {} + + local function queueEvent(event, ...) + table.insert(eventQueue, {event, ...}) + end + + local computer = { + time = function() return apis.os.epoch("utc") end, + clock = function() return apis.os.clock() * 1000 end, + 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 + } + + local icolors = { + [0x1] = 1, -- #000000 + [0x2] = 2, -- #FFFFFF + [0x4] = 3, -- #FF0000 + [0x8] = 4, -- #00FF00 + [0x10] = 5, -- #0000FF + [0x20] = 6, -- #00FFFF + [0x40] = 7, -- #FF00FF + [0x80] = 8, -- #FFFF00 + [0x100] = 9, -- #FF6D00 + [0x200] = 10, -- #6DFF55 + [0x400] = 11, -- #24FFFF + [0x800] = 12, -- #924900 + [0x1000] = 13, -- #6D6D55 + [0x2000] = 14, -- #DBDBAA + [0x4000] = 15, -- #6D00FF + [0x8000] = 16 -- #B6FF00 + } + + local colors = { + 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(0x8000) + apis.term.setTextColor(0x1000) + apis.term.clear() + apis.term.setCursorPos(1, 1) + + local kernelCoro = coroutine.create(function() + ---@diagnostic disable-next-line: param-type-mismatch + 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) + + 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] and ret[2] == "timeout" then + return "timeout" + elseif ret[1] == false then + return "error", ret[2] + else + debug.sethook(co) + return "success", table.unpack(ret, 2) + end + end + + write("Loaded in " .. tostring(apis.os.clock()) .. " seconds.\n") + + 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] == "key" then + queueEvent("keyPressed", 1, event[2]) + if acekeys[event[2]] then + queueEvent("keyTyped", 1, acekeys[event[2]]) + end + elseif event[1] == "char" then + queueEvent("keyTyped", 1, event[2]) + 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] == "modem_message" then + queueEvent("modem_message", table.unpack(event, 2)) + elseif event[1] == "rednet_message" then + queueEvent("rednet_message", table.unpack(event, 2)) + elseif event[1] == "http_success" then + queueEvent("http_success", table.unpack(event, 2)) + elseif event[1] == "http_failure" then + queueEvent("http_failure", table.unpack(event, 2)) + elseif event[1] == "NoSleep" then + exit = true + end + end + if status == "error" 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 diff --git a/Src/Hyperion-firmware-ccpc/boot/cct/eeprom b/Src/Hyperion-firmware-ccpc/boot/cct/eeprom new file mode 100644 index 0000000..e3bb2a4 --- /dev/null +++ b/Src/Hyperion-firmware-ccpc/boot/cct/eeprom @@ -0,0 +1,119 @@ +--:Minify:-- +local BOOT_DRIVE_PATH=({...})[1] or "/$" +-- 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 = {...} +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 +-- 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_DRIVE_PATH.."/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(BOOT_DRIVE_PATH) + 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 \ No newline at end of file diff --git a/Src/Hyperion-firmware-ccpc/boot/cct/initdisks b/Src/Hyperion-firmware-ccpc/boot/cct/initdisks new file mode 100644 index 0000000..3669757 --- /dev/null +++ b/Src/Hyperion-firmware-ccpc/boot/cct/initdisks @@ -0,0 +1,155 @@ +-- :Minify:-- +local apis = ({...})[1] +local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$" +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 + +local disks = {} +local internal = {} + +local function norm(path) + if not path or path == "" then return "/" end + return fs.combine("/", path) +end + +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: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:type(path) + local p = fs.combine(basePath, path) + if not fs.exists(p) then + return nil + elseif fs.isDir(p) then + return "directory" + else + return "file" + end + 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:attributes(path) + local p = fs.combine(basePath, path) + return fs.attributes(p) + end + + function disk:open(path, mode) + local p = fs.combine(basePath, path) + return fs.open(p, mode) + end + + return disk +end + +internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, { + setLabel = function(label) + local h = fs.open("/.label", "w") + h.write(label) + h.close() + end, + getLabel = function() + local h = fs.open("/.label", "r") + if not h then return "$" end + local label = h.readAll() + h.close() + return label + end +}) + +local function refresh() + for id, _ in pairs(disks) do + if not peripheral.getType(id) then disks[id] = nil end + end + + 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 + +local function iter() + refresh() + 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 + +return {refresh = refresh, list = iter} diff --git a/Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/25_tty.kmod b/Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/25_tty.kmod new file mode 100644 index 0000000..8b586e1 --- /dev/null +++ b/Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/25_tty.kmod @@ -0,0 +1,394 @@ +--:Minify:-- +local kernel = ... +local apis = kernel.apis +local native = apis.peripheral +local sides = {"top", "bottom", "left", "right", "front", "back"} +local peripheral={} + +function peripheral.getNames() + local results = {} + for n = 1, #sides do + local side = sides[n] + if native.isPresent(side) then + table.insert(results, side) + if native.hasType(side, "peripheral_hub") then + local remote = native.call(side, "getNamesRemote") + for _, name in ipairs(remote) do + table.insert(results, name) + end + end + end + end + return results +end + +function peripheral.isPresent(name) + if native.isPresent(name) then + return true + end + + for n = 1, #sides do + local side = sides[n] + if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then + return true + end + end + return false +end + +function peripheral.getType(peripheral) + if type(peripheral) == "string" then + if native.isPresent(peripheral) then + return native.getType(peripheral) + end + for n = 1, #sides do + local side = sides[n] + if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then + return native.call(side, "getTypeRemote", peripheral) + end + end + return nil + else + local mt = getmetatable(peripheral) + if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then + error("bad argument #1 (table is not a peripheral)", 2) + end + return table.unpack(mt.types) + end +end + +function peripheral.hasType(peripheral, peripheral_type) + if type(peripheral) == "string" then + if native.isPresent(peripheral) then + return native.hasType(peripheral, peripheral_type) + end + for n = 1, #sides do + local side = sides[n] + if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then + return native.call(side, "hasTypeRemote", peripheral, peripheral_type) + end + end + return nil + else + local mt = getmetatable(peripheral) + if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then + error("bad argument #1 (table is not a peripheral)", 2) + end + return mt.types[peripheral_type] ~= nil + end +end + +function peripheral.getMethods(name) + if native.isPresent(name) then + return native.getMethods(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, "getMethodsRemote", name) + end + end + return nil +end + +function peripheral.getName(peripheral) + local mt = getmetatable(peripheral) + if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then + error("bad argument #1 (table is not a peripheral)", 2) + end + return mt.name +end + +function peripheral.call(name, method, ...) + if native.isPresent(name) then + return native.call(name, method, ...) + 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, "callRemote", name, method, ...) + end + end + return nil +end + +function peripheral.wrap(name) + local methods = peripheral.getMethods(name) + if not methods then + return nil + end + + local types = { peripheral.getType(name) } + for i = 1, #types do types[types[i]] = true end + local result = setmetatable({}, { + __name = "peripheral", + name = name, + type = types[1], + types = types, + }) + for _, method in ipairs(methods) do + result[method] = function(...) + return peripheral.call(name, method, ...) + end + end + return result +end + +function peripheral.find(ty, filter) + local results = {} + for _, name in ipairs(peripheral.getNames()) do + if peripheral.hasType(name, ty) then + local wrapped = peripheral.wrap(name) + if filter == nil or filter(name, wrapped) then + table.insert(results, wrapped) + end + end + end + return table.unpack(results) +end + +local icolors = { + [0x1] = 1, -- #000000 + [0x2] = 2, -- #FFFFFF + [0x4] = 3, -- #FF0000 + [0x8] = 4, -- #00FF00 + [0x10] = 5, -- #0000FF + [0x20] = 6, -- #00FFFF + [0x40] = 7, -- #FF00FF + [0x80] = 8, -- #FFFF00 + [0x100] = 9, -- #FF6D00 + [0x200] = 10, -- #6DFF55 + [0x400] = 11, -- #24FFFF + [0x800] = 12, -- #924900 + [0x1000] = 13, -- #6D6D55 + [0x2000] = 14, -- #DBDBAA + [0x4000] = 15, -- #6D00FF + [0x8000] = 16 -- #B6FF00 +} + +local colors = { + 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 +} + +local function write(text, term) + 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 + + if x > w then + x = 1 + y = y + 1 + end + + if y - 1 >= h then + term.scroll(1) + y = h + term.setCursorPos(x, y) + end + end + + term.setCursorPos(x, y) +end + +kernel.devfs.data.tty={} +local ctrl,alt = false, false + +local function serializeBool(bool) + if bool then + return "T" + else + return "F" + end +end + +local function newtty(obj, id, ev) + kernel.devfs.data["tty"][id] = function(op, mode) + if op=="type" then + return "character device" + elseif op=="open" then + local h = { + read=function(amount) + local rv="" + for i=1, amount or 1 do + local event = {ev()} + if event[1] then + rv=rv..event[1] + end + end + if rv=="" then rv=nil end + return rv + end, + write=function(content) + write(content, obj) + end, + size=function() + local s={obj.getSize()} + return table.concat(s,";") + end, + clear=function() + obj.clear() + obj.setCursorPos(1,1) + end, + gpos=function() + local s={obj.getCursorPos()} + return table.concat(s,";") + end, + spos=function(x,y) + return obj.setCursorPos(x,y) + end, + sfgc=function(c) + return obj.setTextColor(colors[c]) + end, + sbgc=function(c) + return obj.setBackgroundColor(colors[c]) + end, + gfgc=function() + return icolors[obj.getTextColor()] + end, + gbgc=function() + return icolors[obj.getBackgroundColor()] + end, + gctrl=function() + return serializeBool(ctrl)..";"..serializeBool(alt) + end + } + if mode=="rw" then + return h + elseif mode=="r" then + h["write"]=nil + return h + elseif mode=="w" then + h["read"]=nil + return h + end + end + end +end + +local fifo = kernel.newFifo() + +kernel.processes.cctmond = function() + local timeout = false + while true do + local event = {kernel.computer:getMachineEvent()} + + if event[1] then + local eventType = event[1] + local charOrKey = event[3] + + local ctrlKeyMap = { + [apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3, + [apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6, + [apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9, + [apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12, + [apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15, + [apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18, + [apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21, + [apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24, + [apis.keys.y]=25, [apis.keys.z]=26, + } + + if eventType == "keyPressed" then + if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then + ctrl = true + elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then + alt = true + end + + if ctrl then + local ctrlByte = ctrlKeyMap[charOrKey] + if ctrlByte then + if ctrlByte == 3 then + for _, task in ipairs(syscall.getTasks()) do + syscall.sigsend(task, 1) + end + else + fifo.push(string.char(ctrlByte)) + end + end + else + local specialKeyMap = { + [apis.keys.up] = "", + [apis.keys.down] = "", + [apis.keys.right] = "", + [apis.keys.left] = "", + [apis.keys.home] = "", + [apis.keys["end"]] = "", + [apis.keys.pageUp] = "[5~", + [apis.keys.pageDown] = "[6~", + [apis.keys.delete] = "[3~", + } + local special = specialKeyMap[charOrKey] + if special then fifo.push(special) end + end + + elseif eventType == "keyReleased" then + if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then + ctrl = false + elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then + alt = false + end + + elseif eventType == "keyTyped" then + if charOrKey then fifo.push(charOrKey) end + end + + timeout = false + else + timeout = true + end + + if timeout then + sleep(0.05) + end + end +end + +newtty(apis.term, "1", fifo.pop) + +for i,v in ipairs({peripheral.find("monitor")}) do + v.setTextScale(.5) + v.write("Initializing...") + newtty(v,tostring(i+1),function () end) +end \ No newline at end of file diff --git a/Src/Hyperion-installer/@CD/install b/Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_http.kmod similarity index 100% rename from Src/Hyperion-installer/@CD/install rename to Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_http.kmod diff --git a/Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_modem.kmod b/Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_modem.kmod new file mode 100644 index 0000000..e69de29 diff --git a/Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_redstone.kmod b/Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_redstone.kmod new file mode 100644 index 0000000..8e06ca6 --- /dev/null +++ b/Src/Hyperion-firmware-ccpc/lib/modules/CC-Tweaked/40_redstone.kmod @@ -0,0 +1,26 @@ +local args={...} +local kernel=args[1] +local driver={} + +driver.name="CCT Term Module" +driver.version="0.1.0" +driver.type="gpio" +driver.description="CCT redstone Module Kernel Module" +driver.arch="cct" +driver.author="HyperionOS Dev Team" +driver.license="MIT" +driver.api={} + +function driver.load() + -- will +end + +function driver.unload() + -- Nothing to unload +end + +function driver.main() + -- Nothing to run +end + +-- kernel.drivers.register(driver) \ No newline at end of file diff --git a/Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod b/Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod index 8b586e1..8163ad2 100644 --- a/Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod +++ b/Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod @@ -243,6 +243,22 @@ local function serializeBool(bool) end local function newtty(obj, id, ev) + obj.setPaletteColor(0x1, 0xFFFFFF) -- #000000 + obj.setPaletteColor(0x2, 0xFF0000) -- #FFFFFF + obj.setPaletteColor(0x4, 0x00FF00) -- #FF0000 + obj.setPaletteColor(0x8, 0x0000FF) -- #00FF00 + obj.setPaletteColor(0x10, 0x00FFFF) -- #0000FF + obj.setPaletteColor(0x20, 0xFF00FF) -- #00FFFF + obj.setPaletteColor(0x40, 0xFFFF00) -- #FF00FF + obj.setPaletteColor(0x80, 0xFF6D00) -- #FFFF00 + obj.setPaletteColor(0x100, 0x6DFF55) -- #FF6D00 + obj.setPaletteColor(0x200, 0x24FFFF) -- #6DFF55 + obj.setPaletteColor(0x400, 0x924900) -- #24FFFF + obj.setPaletteColor(0x800, 0x6D6D55) -- #924900 + obj.setPaletteColor(0x1000, 0xDBDBAA) -- #6D6D55 + obj.setPaletteColor(0x2000, 0x6D00FF) -- #DBDBAA + obj.setPaletteColor(0x4000, 0xB6FF00) -- #6D00FF + obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00 kernel.devfs.data["tty"][id] = function(op, mode) if op=="type" then return "character device" diff --git a/Src/Hyperion-kernel/boot/kernel.lua b/Src/Hyperion-kernel/boot/kernel.lua index 28a03ef..19a380d 100644 --- a/Src/Hyperion-kernel/boot/kernel.lua +++ b/Src/Hyperion-kernel/boot/kernel.lua @@ -27,15 +27,15 @@ local windowsExp = false function kernel.log(msg, level, c) c=c or 12 - kernel.LOG_Text = kernel.LOG_Text..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n" + kernel.LOG_Text = kernel.LOG_Text..string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n" if kernel.status == "start" then screen:setTextColor(c) - screen:print(string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg) + screen:print(tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg) elseif kernel.status == "term" then kernel.standbyTask=kernel.currentTask kernel.currentTask=kernel.kernelTask kernel.vfs.devctl(1,"sfgc",c) - kernel.vfs.write(1,string.format("%X",c-1).." "..tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n") + kernel.vfs.write(1,tostring(computer:time()).." "..kernel.users[kernel.uid].." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n") kernel.currentTask=kernel.standbyTask end end @@ -149,6 +149,10 @@ function kernel.saveLog() ifs.writeAllText("/var/log/syslog.log", kernel.LOG_Text) end +function loadcstr(string) + +end + function kernel.newFifo() local fifo = {} fifo.push=function(data) @@ -251,7 +255,12 @@ kernel.syscalls["sysdump"]=function() end return rv end -kernel.syscalls["test"]=function() return true end +kernel.syscalls["reboot"]=function() + kernel.computer:reboot() +end +kernel.syscalls["shutdown"]=function() + kernel.computer:reboot() +end kernel.log("Running modules") for _,p in ipairs(modules) do @@ -272,6 +281,7 @@ for _,p in ipairs(modules) do end kernel.log("Kernel initialized successfully.") +kernel.saveLog() kernel.status="running" kernel.main() if kernel.status=="panic" then diff --git a/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod index fd63bc9..07b6617 100644 --- a/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod +++ b/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod @@ -401,12 +401,27 @@ local function getFileMeta(path, noFollow) return { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" } end - local parent, name = real:match("^(.*)/([^/]+)$") - if not parent or parent == "" then parent = "/" end + local cur = real + + -- FML i hated implementing this - Astronand + while true do + local parent, name = cur:match("^(.*)/([^/]+)$") + if not parent or parent == "" then parent = "/" end + + local disk, parentDiskPath = resolveMount(parent) + local entry = readMetaEntry(disk, parentDiskPath, name) + + if entry then + return entry + end + + if parent == "/" or cur == "/" then + break + end + + cur = parent + end - local disk, parentDiskPath = resolveMount(parent) - local entry = readMetaEntry(disk, parentDiskPath, name) - if entry then return entry end return { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" } end @@ -610,6 +625,7 @@ function vfs.open(path, mode) if mode == "r" and bit_is_set(meta.perms, 6) then fobj.suid_owner = meta.owner end + if disk.isvirt then fobj.isvirt=true end task.fd[fd] = fobj if not disk.isvirt then total = total + 1 end return fd @@ -658,8 +674,10 @@ function vfs.close(fd) local task = kernel.currentTask local file = task.fd[fd] if not file then error("EBADF") end + if not task.fd[fd].isvirt then + total = total - 1 + end task.fd[fd] = nil - total = total - 1 file.refcount = file.refcount - 1 if file.refcount <= 0 and file.handle and file.handle.close then file.handle.close() diff --git a/Src/Hyperion-kernel/lib/modules/hyperion/12_devfs.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/12_devfs.kmod index 50d705b..6ca94fa 100644 --- a/Src/Hyperion-kernel/lib/modules/hyperion/12_devfs.kmod +++ b/Src/Hyperion-kernel/lib/modules/hyperion/12_devfs.kmod @@ -16,7 +16,7 @@ proxy.getLabel = function() return "devfs" end proxy.attributes = function(path) return { size = 0, modified = 0, - created = 0, + created = 0 } end function proxy:open(path, mode) diff --git a/Src/Hyperion-kernel/lib/modules/hyperion/92_permissions.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/92_permissions.kmod index dd38e96..17f59da 100644 --- a/Src/Hyperion-kernel/lib/modules/hyperion/92_permissions.kmod +++ b/Src/Hyperion-kernel/lib/modules/hyperion/92_permissions.kmod @@ -87,7 +87,7 @@ kernel.log("Seeding filesystem permissions...", "INFO") mergeMeta("/", { {"bin", REG, 0, 0, RWX_RX_RX}, {"boot", REG, 0, 0, RWX_RX_RX}, - {"dev", REG, 0, 0, RWX_RX_RX}, + {"dev", REG, 0, 0, RWXRWXRWX}, {"etc", REG, 0, 0, RWX_RX_RX}, {"home", REG, 0, 0, RWX_RX_RX}, {"lib", REG, 0, 0, RWX_RX_RX}, @@ -98,126 +98,10 @@ mergeMeta("/", { {"var", REG, 0, 0, RWX_RX_RX}, }) -mergeMeta("/boot", { - {"kernel.lua", REG, 0, 0, RW_R_R }, - {"boot.cfg", REG, 0, 0, RW_R_R }, - {"safeboot.cfg", REG, 0, 0, RW_R_R }, - {"fstab", REG, 0, 0, RW_R_R }, - {"initfs", REG, 0, 0, RW_R_R }, - {"cct", REG, 0, 0, RWX_RX_RX}, - {"oc", REG, 0, 0, RWX_RX_RX}, -}) - -mergeMeta("/boot/cct", { - {"boot.lua", REG, 0, 0, RW_R_R}, - {"initdisks", REG, 0, 0, RW_R_R}, - {"eeprom", REG, 0, 0, RW_R_R}, -}) - -mergeMeta("/boot/oc", { - {"boot.lua", REG, 0, 0, RW_R_R}, - {"initfs.lua",REG, 0, 0, RW_R_R}, - {"eeprom", REG, 0, 0, RW_R_R}, -}) - -mergeMeta("/sbin", { - {"init.lua", REG, 0, 0, RWX_RX_RX}, -}) - mergeMeta("/bin", { - {"cat", REG, 0, 0, RWX_RX_RX}, - {"chattr", REG, 0, 0, RWX_RX_RX}, - {"chgrp", REG, 0, 0, RWX_RX_RX}, - {"chmod", REG, 0, 0, RWX_RX_RX}, - {"chown", REG, 0, 0, RWX_RX_RX}, - {"chroot", REG, 0, 0, RWX_RX_RX}, - {"clear", REG, 0, 0, RWX_RX_RX}, - {"echo", REG, 0, 0, RWX_RX_RX}, - {"hfetch", REG, 0, 0, RWX_RX_RX}, - {"help", REG, 0, 0, RWX_RX_RX}, - {"hysh", REG, 0, 0, RWX_RX_RX}, - {"hyshex", REG, 0, 0, RWX_RX_RX}, - {"id", REG, 0, 0, RWX_RX_RX}, - {"install", REG, 0, 0, RWX_RX_RX}, - {"ln", REG, 0, 0, RWX_RX_RX}, {"login", REG, 0, 0, SUID_755 }, - {"loimgcreate", REG, 0, 0, RWX_RX_RX}, - {"looptest", REG, 0, 0, RWX_RX_RX}, - {"losetup", REG, 0, 0, RWX_RX_RX}, - {"ls", REG, 0, 0, RWX_RX_RX}, - {"lsusers", REG, 0, 0, RWX_RX_RX}, - {"lua", REG, 0, 0, RWX_RX_RX}, - {"luaold", REG, 0, 0, RWX_RX_RX}, - {"micro", REG, 0, 0, RWX_RX_RX}, - {"mkdir", REG, 0, 0, RWX_RX_RX}, - {"mount", REG, 0, 0, RWX_RX_RX}, - {"passwd", REG, 0, 0, RWX_RX_RX}, - {"ps", REG, 0, 0, RWX_RX_RX}, - {"pwd", REG, 0, 0, RWX_RX_RX}, - {"readlink", REG, 0, 0, RWX_RX_RX}, - {"sed", REG, 0, 0, RWX_RX_RX}, - {"socktest", REG, 0, 0, RWX_RX_RX}, - {"spm", REG, 0, 0, RWX_RX_RX}, - {"startup", REG, 0, 0, RWX_RX_RX}, {"su", REG, 0, 0, SUID_755 }, {"sudo", REG, 0, 0, SUID_755 }, - {"sysdump", REG, 0, 0, RWX_RX_RX}, - {"umount", REG, 0, 0, RWX_RX_RX}, - {"useradd", REG, 0, 0, RWX_RX_RX}, - {"userdel", REG, 0, 0, RWX_RX_RX}, - {"usermod", REG, 0, 0, RWX_RX_RX}, - {"whoami", REG, 0, 0, RWX_RX_RX}, - {"yes", REG, 0, 0, RWX_RX_RX}, -}) - -mergeMeta("/bin/startup", { - {"test.lua", REG, 0, 0, RWX_RX_RX}, -}) - -mergeMeta("/lib", { - {"sys", REG, 0, 0, RWX_RX_RX}, - {"modules", REG, 0, 0, RWX_RX_RX}, - {"crypto", REG, 0, 0, RWX_RX_RX}, - {"store", REG, 0, 0, RWX_RX_RX}, - {"snip", REG, 0, 0, RW_R_R }, - {"io", REG, 0, 0, RW_R_R }, - {"bit32", REG, 0, 0, RW_R_R }, -}) - -mergeMeta("/lib/sys", { - {"fs", REG, 0, 0, RW_R_R}, - {"hpv", REG, 0, 0, RW_R_R}, - {"ipc", REG, 0, 0, RW_R_R}, - {"term", REG, 0, 0, RW_R_R}, - {"init", REG, 0, 0, RW_R_R}, -}) - -mergeMeta("/lib/modules", { - {"hyperion", REG, 0, 0, RWX_RX_RX}, -}) - -mergeMeta("/lib/modules/hyperion", { - {"01_stdlib.kmod", REG, 0, 0, RW_R_R}, - {"10_vfs.kmod", REG, 0, 0, RW_R_R}, - {"11_require.kmod", REG, 0, 0, RW_R_R}, - {"12_devfs.kmod", REG, 0, 0, RW_R_R}, - {"12_tmpfs.kmod", REG, 0, 0, RW_R_R}, - {"13_loopdev.kmod", REG, 0, 0, RW_R_R}, - {"14_keventd.kmod", REG, 0, 0, RW_R_R}, - {"19_fstab.kmod", REG, 0, 0, RW_R_R}, - {"20_signals.kmod", REG, 0, 0, RW_R_R}, - {"20_socket.kmod", REG, 0, 0, RW_R_R}, - {"26_tty.kmod", REG, 0, 0, RW_R_R}, - {"30_userspace.kmod", REG, 0, 0, RW_R_R}, - {"40_auth.kmod", REG, 0, 0, RW_R_R}, - {"45_hypervisor.kmod", REG, 0, 0, RW_R_R}, - {"47_dbg.kmod", REG, 0, 0, RW_R_R}, - {"50_gpio.kmod", REG, 0, 0, RW_R_R}, - {"70_stdlibadv.kmod", REG, 0, 0, RW_R_R}, - {"90_init.kmod", REG, 0, 0, RW_R_R}, - {"91_login.kmod", REG, 0, 0, RW_R_R}, - {"92_permissions.kmod", REG, 0, 0, RW_R_R}, - {"99_final.kmod", REG, 0, 0, RW_R_R}, }) mergeMeta("/etc", { diff --git a/build.py b/build.py index e04aeea..aee6698 100644 --- a/build.py +++ b/build.py @@ -6,8 +6,10 @@ Usage: Targets: build build-mini + build-micro build-test build-mini-test + build-micro-test clean Arch flags: @@ -23,11 +25,9 @@ import sys import shutil import argparse import subprocess -import hashlib -import random -import string from pathlib import Path from typing import Union +import lz4.frame PROJECT_ROOT = Path(__file__).resolve().parent SRC_ROOT = PROJECT_ROOT / "Src" @@ -48,7 +48,34 @@ def clean(): print("Nothing to clean.") -def process_root(src_root: Path, out_root: Path, minify: bool): +def has_minify_header(path: Path) -> bool: + try: + with path.open("r", encoding="utf-8", errors="ignore") as f: + for _ in range(3): + if "--:Minify:--" in f.readline(): + return True + except OSError: + pass + return False + + +def minify_file(src: Path) -> str: + result = subprocess.run( + ["luamin.cmd", "-f", str(src)], + capture_output=True, + text=True + ) + if result.returncode != 0: + print(f" ! luamin failed: {result.stderr.strip()}", file=sys.stderr) + sys.exit(1) + return result.stdout + + +def compress_lz4(data: bytes) -> bytes: + return lz4.frame.compress(data) + + +def process_root(src_root: Path, out_root: Path, minify: bool, micro: bool): print(f"Building from {src_root}") print(f"Output to {out_root}") print() @@ -69,16 +96,20 @@ def process_root(src_root: Path, out_root: Path, minify: bool): print(f" Processing: {src.relative_to(src_root)}") - if minify and has_minify_header(src): + if has_minify_header(src): print(" > Minifying") - result = subprocess.run( - ["luamin.cmd", "-f", str(src)], - capture_output=True, text=True - ) - if result.returncode != 0: - print(f" ! luamin failed: {result.stderr.strip()}", file=sys.stderr) - sys.exit(1) - dst.write_text(result.stdout, encoding="utf-8") + content = minify_file(src) + if micro: + print(" > LZ4 compressing") + compressed = compress_lz4(content.encode("utf-8")) + # wrap in kernel.unpack if in hyperion-kernel + if pkg_dir.name == "hyperion-kernel" and dst.suffix == ".lua": + content_str = f"kernel.unpack([=[{compressed.hex()}]=])" + dst.write_text(content_str, encoding="utf-8") + else: + dst.write_bytes(compressed) + else: + dst.write_text(content, encoding="utf-8") else: print(" > Copying") shutil.copy2(src, dst) @@ -95,26 +126,15 @@ def install_bootloader(arch: str, release: bool): shutil.copy2(eeprom, BUILD_ROOT / eeprom_dst_name) -def has_minify_header(path: Path) -> bool: - try: - with path.open("r", encoding="utf-8", errors="ignore") as f: - for _ in range(3): - if "--:Minify:--" in f.readline(): - return True - except OSError: - pass - return False - - -def run_build(minify: bool, include_test: bool, arch: Union[str, None], release: bool): +def run_build(minify: bool, micro: bool, include_test: bool, arch: Union[str, None], release: bool): clean() BUILD_ROOT.mkdir() out_root = BUILD_ROOT / "$" if arch else BUILD_ROOT - process_root(SRC_ROOT, out_root, minify) + process_root(SRC_ROOT, out_root, minify, micro) if include_test: - process_root(TEST_ROOT, out_root, minify) + process_root(TEST_ROOT, out_root, minify, micro) if arch: print("Installing bootloader files ...") @@ -122,46 +142,6 @@ def run_build(minify: bool, include_test: bool, arch: Union[str, None], release: print() -def main(): - parser = argparse.ArgumentParser(description="HyperionOS build script") - parser.add_argument("target", choices=["build", "build-mini", "build-test", "build-mini-test", "clean"]) - parser.add_argument("--arch", choices=["cct", "oc"], default=None, - help="Target architecture (cct or oc)") - parser.add_argument("--release", dest="release", action="store_true", default=True, - help="Release build: eeprom placed as startup.lua (default)") - parser.add_argument("--dev", dest="release", action="store_false", - help="Dev build: boot.lua and eeprom copied unchanged") - parser.add_argument( - "--makeuser", metavar=("USERNAME", "PASSWORD"), nargs=2, action="append", - default=[], - help=( - "Pre-create a user on first boot (dev builds only). " - "May be specified multiple times. " - "Example: --makeuser root secretpass --makeuser alice alicepass" - ), - ) - - args = parser.parse_args() - - if args.makeuser and args.release: - parser.error("--makeuser is only allowed with --dev builds") - - if args.target == "clean": - clean() - return - - minify = "mini" in args.target - include_test = "test" in args.target - - run_build(minify=minify, include_test=include_test, arch=args.arch, release=args.release) - - if args.makeuser: - print("Injecting first-boot user setup ...") - inject_makeusers(args.makeuser, args.arch) - print() - - print("Build complete.") - def _make_firstboot_kmod(users): lines = [] lines.append("local kernel = ...") @@ -212,5 +192,47 @@ def inject_makeusers(users, arch): print(" Wrote first-boot user setup -> " + str(kmod_path.relative_to(BUILD_ROOT))) +def main(): + parser = argparse.ArgumentParser(description="HyperionOS build script") + parser.add_argument("target", choices=["build", "build-mini", "build-micro", "build-test", "build-mini-test", "build-micro-test", "clean"]) + parser.add_argument("--arch", choices=["cct", "oc"], default=None, + help="Target architecture (cct or oc)") + parser.add_argument("--release", dest="release", action="store_true", default=True, + help="Release build: eeprom placed as startup.lua (default)") + parser.add_argument("--dev", dest="release", action="store_false", + help="Dev build: boot.lua and eeprom copied unchanged") + parser.add_argument( + "--makeuser", metavar=("USERNAME", "PASSWORD"), nargs=2, action="append", + default=[], + help=( + "Pre-create a user on first boot (dev builds only). " + "May be specified multiple times. " + "Example: --makeuser root secretpass --makeuser alice alicepass" + ), + ) + + args = parser.parse_args() + + if args.makeuser and args.release: + parser.error("--makeuser is only allowed with --dev builds") + + if args.target == "clean": + clean() + return + + minify = "mini" in args.target or "micro" in args.target + micro = "micro" in args.target + include_test = "test" in args.target + + run_build(minify=minify, micro=micro, include_test=include_test, arch=args.arch, release=args.release) + + if args.makeuser: + print("Injecting first-boot user setup ...") + inject_makeusers(args.makeuser, args.arch) + print() + + print("Build complete.") + + if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/building.md b/building.md index 585f45e..f26e86c 100644 --- a/building.md +++ b/building.md @@ -8,24 +8,23 @@ Run: make build ``` -Optional variables: +**Optional variables:** -* **`ARCH=`** +* **`ARCH=`** – Select bootloader type: - * `cct` Build using the cct bootloader - * `oc` Build using the oc bootloader + * `cct` – Build using the CCT bootloader + * `oc` – Build using the OC bootloader -* **`DEV=1`** +* **`DEV=1`** – Enable development mode: - * Builds in development mode - * Bootloader does not start automatically on system startup + * Bootloader does **not** start automatically on system startup -If `DEV` is not specified: +**Default behavior (if `DEV` is not specified):** -* Default is release mode -* Bootloader starts automatically on system startup +* Builds in **release mode** +* Bootloader starts automatically on system startup -**Examples** +**Examples:** ```bash make build ARCH=cct @@ -42,38 +41,46 @@ Run: python build.py build ``` -Optional arguments: +**Optional arguments:** -* **`--arch {cct|oc}`** - Select bootloader +* **`--arch {cct|oc}`** – Select bootloader: - * `cct` Use the cct bootloader - * `oc` Use the oc bootloader + * `cct` – Use the CCT bootloader + * `oc` – Use the OC bootloader -* **`--dev`** +* **`--dev`** – Development mode: - * Development mode - * Bootloader does not start automatically. You must run `eeprom` in CraftOS to start Hyperion. + * Bootloader does **not** start automatically + * You must run `eeprom` in CraftOS to start Hyperion -* **`--release`** (default) +* **`--release`** (default) – Release mode: - * Release mode * Bootloader starts automatically - -* **`--makeuser username password`** - Makes a username upon startup. Only works for `--dev` builds. - - * `--makeuser root rootpass` - - Makes the root account already exist on first boot with rootpass as password - - * `--makeuser root rootpass --makeuser alice alicepass` - - Makes the root account and alice account already exist on first boot with defined passwords -**Examples** +* **`--makeuser username password`** – Pre-create user accounts (only works with `--dev` builds): + + ```bash + --makeuser root rootpass + --makeuser root rootpass --makeuser alice alicepass + ``` + + * Example: The first command creates the `root` account with the given password on first boot + * Example: The second command creates both `root` and `alice` accounts with defined passwords on first boot + +**Examples:** ```bash python build.py build --arch cct python build.py build --arch oc --dev ``` + +--- + +### Build Requirements + +* **`build`** – No additional requirements +* **`build-mini`** – Requires [`luamin`](https://www.npmjs.com/package/luamin) +* **`build-micro`** – Requires: + + * [`luamin`](https://www.npmjs.com/package/luamin) + * [`LZ4 binaries`](https://github.com/lz4/lz4/releases) \ No newline at end of file