From 876e5160dfc85335294dbd86928d716b112869b0 Mon Sep 17 00:00:00 2001 From: Ponali Date: Fri, 11 Jul 2025 11:30:52 +0200 Subject: [PATCH] Updated the Halyde web installer to work on read-only floppies, now with a graphical environment --- argentum.cfg | 1 + webinstall.lua | 294 ++++++++++++++++++++++++++++++++++++------------- 2 files changed, 221 insertions(+), 74 deletions(-) diff --git a/argentum.cfg b/argentum.cfg index 4491480..0ef212b 100644 --- a/argentum.cfg +++ b/argentum.cfg @@ -10,6 +10,7 @@ local agcfg = { "halyde/core", "halyde/drivers", "halyde/lib", + "halyde" "home", "mnt" }, diff --git a/webinstall.lua b/webinstall.lua index 3f3b565..5ac5425 100644 --- a/webinstall.lua +++ b/webinstall.lua @@ -7,56 +7,142 @@ local internet = component.internet local computer = require("computer") local fs = require("filesystem") local gpu = component.gpu +local width,height = gpu.getResolution() +local event = require("event") +local keyboard = require("keyboard") local installLocation +local installAddress local drives = {} +local driveAddresses = {} for drive in fs.list("/mnt/") do - table.insert(drives, drive) + local address = component.get(drive:sub(1, 3), "filesystem") + if not component.invoke(address, "isReadOnly") then + table.insert(drives, drive) + table.insert(driveAddresses,address) + end end -if #drives == 1 and not component.invoke(component.get(drives[1]:sub(1, 3), "filesystem"), "isReadOnly") then - installLocation = "/mnt/" .. drives[1] -elseif #drives == 1 then - io.stderr.write("All drives are read-only.\nHalyde cannot be installed.") -else - local installDrivesText = "Possible drives to install to:" - for i = 1, #drives do - local address = component.get(drives[i]:sub(1, 3), "filesystem") - local fsComponent = component.proxy(address) - if not fsComponent.isReadOnly() then - local label = fsComponent.getLabel() - if label then - installDrivesText = installDrivesText .. "\n " .. tostring(i) .. ". - " .. label .. "(" .. address:sub(1, 5) .. "...)" - else - installDrivesText = installDrivesText .. "\n " .. tostring(i) .. ". - " .. address:sub(1, 5) .. "..." - end - else - table.remove(drives, i) - i = i - 1 - end + +local function reset() + gpu.setBackground(0x000000) + gpu.setForeground(0xFFFFFF) + gpu.fill(1,1,width,height," ") + require("tty").setCursor(1,1) +end + +local function chooseDrive() + local driveLabels = {} + for i,drive in ipairs(drives) do + local address = driveAddresses[i] + table.insert(driveLabels,component.invoke(address,"getLabel")) end - io.write(installDrivesText .. "\nPlease select a drive by entering its number or \"q\" to quit. ") - local answer + + gpu.set(1,1,"Please select a drive to install Halyde to:") + + local cur = 1 + local function renderItem(idx,cur) + if cur then + gpu.setBackground(0xFFFFFF) + gpu.setForeground(0x000000) + else + gpu.setBackground(0x000000) + gpu.setForeground(0xFFFFFF) + end + gpu.fill(4,2+idx,width-3,1," ") + local label = driveLabels[idx] or "No label" + gpu.set(4,2+idx,label) + gpu.set(4+#label+1,2+idx,driveAddresses[idx]:sub(1,5).."...") + end + + local function moveCur(dir) + local ncur = math.max(math.min(cur+dir,#drives),1) + if cur==ncur then return end + renderItem(ncur,true) + renderItem(cur,false) + cur=ncur + end + + gpu.fill(2,3,1,#drives,"*") + for i=1,#drives do + renderItem(i,cur==i) + end + while true do - answer = io.read() - if tonumber(answer) and tonumber(answer) >= 1 and tonumber(answer) <= #drives then + local args = {event.pull("key_down")} + if not args or not args[1] or not args[4] then goto continue end + + local key = keyboard.keys[args[4]] + if key=="up" then + moveCur(-1) + elseif key=="down" then + moveCur(1) + elseif key=="right" or key=="enter" then + gpu.setBackground(0x000000) + gpu.setForeground(0xFFFFFF) + gpu.fill(1,1,width,height-1," ") break - elseif answer == "q" then - return - else - print("Answer invalid, try again.") + elseif key=="left" or key=="back" then + return false end + ::continue:: + end + + installLocation = "/mnt/" .. drives[cur] + installAddress = driveAddresses[cur] + return true +end + +if #drives == 0 then + io.stderr.write("All drives are read-only.\nHalyde cannot be installed.") +elseif #drives == 1 then + installLocation = "/mnt/" .. drives[1] +end + +gpu.fill(1,1,width,height," ") +gpu.setBackground(0xFFFFFF) +gpu.setForeground(0x000000) +gpu.fill(1,height,width,1," ") +gpu.set(1,height,"Halyde Web Installer (OpenOS)") +gpu.setBackground(0x000000) +gpu.setForeground(0xFFFFFF) + +if #drives>1 then + if not chooseDrive() then + reset() + return end - installLocation = "/mnt/" .. drives[tonumber(answer)] end if not installLocation then - print("All drives are read-only.\nHalyde cannot be installed.") - return -end -io.write("Are you sure you would like to install Halyde to " .. installLocation .. "? This will erase all data on this disk. [Y/n] ") -if io.read():lower() == "n" then + reset() + io.stderr.write("All drives are read-only.\nHalyde cannot be installed.") return end +if width<80 then + gpu.set(1,1,"Are you sure you would like to install Halyde?") +else + gpu.set(1,1,"Are you sure you would like to install Halyde to "..installLocation.."?") +end +gpu.set(1,2,"This will erase all data on this disk.") +gpu.set(1,height-1,"Press Y to accept, or N to cancel.") +gpu.set(3,4,"Capacity: ") +gpu.set(3,5,"Used: ") +gpu.set(3,6,"ID: ") +gpu.set(3,7,"Label: ") +gpu.setForeground(0x00FF00) +if width>=80 then + gpu.set(50,1,installLocation) +end +gpu.set(13,4,math.floor(component.invoke(installAddress,"spaceTotal")/1024).." KiB") +gpu.set(9,5,math.floor(component.invoke(installAddress,"spaceUsed")/1024).." KiB") +gpu.set(7,6,installAddress) +gpu.set(10,7,component.invoke(installAddress,"getLabel") or "No label") + +if keyboard.keys[({event.pull("key_down")})[4]]=="n" then + return reset() +end + -- installation + local computer = require("computer") local function getFile(url) @@ -79,42 +165,86 @@ local function getFile(url) return data end -local function getFile(path) - if path:sub(1,1) == "/" then - if not fs.exists(path) then - return false, "file does not exist" - end - local handle, data, tmpdata = fs.open(path, "r"), "", nil - repeat - tmpdata = handle:read(math.huge) - data = data .. (tmpdata or "") - until not tmpdata - handle:close() - return data - else - local request, data, tmpdata = nil, "", nil - local status, errorMessage = pcall(function() - request = internet.request(path) - request:finishConnect() - end) - if not status then - return false, errorMessage - end - local responseCode = request:response() - if responseCode and responseCode ~= 200 then - return false, responseCode - end - repeat - tmpdata = request.read(math.huge) - data = data .. (tmpdata or "") - until not tmpdata - return data - end +-- installation graphics +local webInstallConfig +local installationOrder = {"halyde", "edit", "argentum", "webinstall-extras"} + +gpu.setBackground(0x000000) +gpu.setForeground(0xFFFFFF) +gpu.fill(1,1,width,height," ") + +local function lpad(str, len, char) + str=tostring(str) + if char == nil then char = ' ' end + return string.rep(char, len - #str) .. str end -local webInstallConfig = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/argentum.cfg") + +local function progress(package,progress) + local total = 0 + if webInstallConfig and type(webInstallConfig)=="table" then + for i,pck in ipairs(installationOrder) do + local packConfig = webInstallConfig[pck] + -- print(pck,packConfig) + total=total+#(packConfig.directories or {})+#(packConfig.files or {}) + end + else + total=1 + end + + local info = "" + local realProgress = 1 + if type(package)=="string" then + realProgress = progress + info = string.format("%s %s%%",package,lpad(math.floor(progress*100),2)) + else + realProgress = 0 + for i=1,package do + local packConfig = webInstallConfig[installationOrder[i]] + if i==package then + realProgress=realProgress+progress + else + local value = #(packConfig.directories or {})+#packConfig.files + realProgress=realProgress+value + end + end + realProgress=realProgress/total + local packConfig = webInstallConfig[installationOrder[package]] + progress=progress/(#(packConfig.directories or {})+#packConfig.files) + -- realProgress = (progress+package-1)/#installationOrder + local packInfo = installationOrder[package].." "..lpad(math.floor(progress*100),2).."%" + info = string.format("%s%% [%s]",lpad(math.floor(realProgress*100),2),packInfo) + end + + info=info..string.rep(" ",width-#info) + local progX = math.floor(realProgress*width) + gpu.setBackground(0x00FF00) + gpu.setForeground(0x000000) + gpu.set(1,height,info:sub(1,progX)) + gpu.setBackground(0x000000) + gpu.setForeground(0xFFFFFF) + gpu.set(progX+1,height,info:sub(progX+1)) +end +local logY = 1 +local function log(txt) + if logY>=height then + gpu.copy(1,2,width,height-2,0,-1) + gpu.fill(1,height-1,width,1," ") + logY=logY-1 + end + gpu.set(1,logY,txt) + logY=logY+1 +end +---------------------------- + +log("Fetching Argentum configuration for Halyde") +progress("Preparing",0) +webInstallConfig = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/argentum.cfg") +log("Loading Argentum configuration") +progress("Preparing",0.5) webInstallConfig = load(webInstallConfig) webInstallConfig = webInstallConfig() -local installationOrder = {"halyde", "edit", "argentum", "webinstall-extras"} +log("Looking for outdated files in the drive") +progress("Preparing",1) local oldFiles = {} for oldFile in fs.list(installLocation) do local usedFlag = false @@ -132,29 +262,45 @@ for oldFile in fs.list(installLocation) do end end end + if oldFile=="halyde/" then usedFlag = true end if not usedFlag then table.insert(oldFiles, oldFile) end end +log("Found "..#oldFiles) +progress(1,0) + for i = 1, 4 do local webInstallConfig = webInstallConfig[installationOrder[i]] + local dirCount = 0 if webInstallConfig.directories then - for _, directory in pairs(webInstallConfig.directories) do - print("Creating " .. directory .. "...") + dirCount=#webInstallConfig.directories + for dirIdx, directory in ipairs(webInstallConfig.directories) do + log("Creating " .. directory .. "...") + progress(i,dirIdx-1) fs.makeDirectory(installLocation .. directory) end end - for _, file in pairs(webInstallConfig.files) do - print("Downloading " .. file .. "...") + for fileIdx, file in ipairs(webInstallConfig.files) do + log("Downloading " .. file .. "...") + progress(i,fileIdx-1+dirCount) local handle = fs.open(installLocation .. file, "w") handle:write(getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/" .. file)) handle:close() end end -for _, oldFile in pairs(oldFiles) do - fs.remove(oldFile) +for i, oldFile in ipairs(oldFiles) do + log("Removing "..oldFile) + progress("Finishing up",(i-1)/#oldFiles*1) + fs.remove(installLocation .. oldFile) end +log("Setting boot address") +progress("Finishing up",1) computer.setBootAddress(component.get(installLocation:sub(6, -2))) + +log("Setting label to Halyde") component.invoke(component.get(installLocation:sub(6, -2)), "setLabel", "Halyde") + +gpu.fill(1,1,width,height," ") computer.shutdown(true)