-- ============================================================ -- GATOS CASSIOPEIA (v18.5) - DEPLOYMENT WIZARD -- Features: EULA, Store Pathing, Core Control Panel, Exact JSON -- ============================================================ local w, h = term.getSize() local baseURL = "https://install.coraliusrealms.net/" local storeURL = "https://install.coraliusrealms.net/store/v2/" -- ========================================== -- DEFAULT MANIFESTS (Fallbacks) -- ========================================== local coreFiles = { "Menu.lua", "settings.lua", "startup.lua", "upgrade.lua", "addresses/defAdds.lua", "addresses/defNames.lua", "apps/about/about.lua", "apps/about/appdata", "apps/control/control.lua", "apps/control/appdata", -- Added Control Panel "apps/readme_viewer/readme_viewer.lua", "apps/readme_viewer/appdata", "apps/store/store.lua", "apps/store/appdata", "common/clock", "common/readme", "common/savedLocks.json", "common/version", "common/versionName", "common/comp/company", "common/comp/cpu", "common/comp/memory", "sys/theme.json", "sys/apis/gatos.lua", "sys/icons/file.nfp", "sys/icons/folder.nfp", "sys/icons/lua.nfp", "sys/icons/text.nfp", "sys/store/sources.list" } local appCollections = { { id = "sg_ops", name = "SG Operations", apps = {"address_store", "dhd", "dial", "gate", "iris"}, selected = false }, { id = "utils", name = "Extra Utilities", apps = {"calendar", "clock", "explorer", "desktop", "gatpalette", "notepad", "taskmgr"}, selected = false }, { id = "games", name = "GatGames Suite", apps = {"mines", "scores", "sudoku"}, selected = false } } local appFiles = { ["address_store"] = {"address_store/hub.lua", "address_store/appdata"}, ["dhd"] = {"dhd/DHD.lua", "dhd/appdata"}, ["dial"] = {"dial/Dial.lua", "dial/appdata"}, ["gate"] = {"gate/gate.lua", "gate/appdata"}, ["iris"] = {"iris/iris.lua", "iris/appdata", "iris/IDC.lua"}, ["calendar"] = {"calendar/calendar.lua", "calendar/appdata"}, ["clock"] = {"clock/clock.lua", "clock/appdata"}, ["explorer"] = {"explorer/explorer.lua", "explorer/appdata"}, ["desktop"] = {"desktop/desktop.lua", "desktop/appdata"}, ["gatpalette"] = {"gatpalette/gatpalette.lua", "gatpalette/appdata"}, ["notepad"] = {"notepad/notepad.lua", "notepad/appdata"}, ["taskmgr"] = {"taskmgr/taskmgr.lua", "taskmgr/appdata"}, ["mines"] = {"mines/mines.lua", "mines/appdata"}, ["scores"] = {"scores/scores.lua", "scores/appdata"}, ["sudoku"] = {"sudoku/sudoku.lua", "sudoku/appdata"} } local function fetchNetworkData() local mReq = http.get(baseURL .. "manifest.json") if mReq then local data = textutils.unserializeJSON(mReq.readAll()) mReq.close() if data and data.coreFiles and data.collections then coreFiles = data.coreFiles appCollections = data.collections end end local rReq = http.get(storeURL .. "repo.json") if rReq then local data = textutils.unserializeJSON(rReq.readAll()) rReq.close() if data and data.apps then for _, app in ipairs(data.apps) do local cleanedFiles = {} for _, f in ipairs(app.files) do table.insert(cleanedFiles, f:gsub("^apps/", "")) end appFiles[app.id] = cleanedFiles end end end end -- ========================================== -- VISUAL HELPERS -- ========================================== local function center(y, text, textColor, bgColor) term.setCursorPos(math.floor((w - #text) / 2) + 1, y) if textColor then term.setTextColor(textColor) end if bgColor then term.setBackgroundColor(bgColor) end term.write(text) end local function dr(x, y, wid, hei, c) term.setBackgroundColor(c) for i=0, hei-1 do term.setCursorPos(x, y+i) term.write(string.rep(" ", wid)) end end local function dt(x, y, t, f, b) term.setCursorPos(x, y) if f then term.setTextColor(f) end if b then term.setBackgroundColor(b) end term.write(t) end local function clearScreen() term.setBackgroundColor(colors.black) term.clear() end local function drawHeader(title) clearScreen() dr(1, 1, w, 1, colors.gray) dt(2, 1, "GatOS Deployment Wizard - " .. title, colors.cyan, colors.gray) end -- ========================================== -- STEP 1 & 2: TEXT READERS -- ========================================== local function readDocView(title, lines) local offset = 0 local function drawDoc() drawHeader(title) term.setBackgroundColor(colors.black) term.setTextColor(colors.white) for i = 1, h - 3 do if lines[i + offset] then term.setCursorPos(2, i + 1) term.write(string.sub(lines[i + offset], 1, w - 2)) end end dr(1, h, w, 1, colors.gray) dr(math.floor(w/2)-9, h, 19, 1, colors.blue) dt(math.floor(w/2)-8, h, " ACCEPT & CONTINUE ", colors.white, colors.blue) end drawDoc() while true do local e, p1, p2, p3 = os.pullEvent() if e == "mouse_scroll" then local maxScroll = math.max(0, #lines - (h - 3)) offset = math.max(0, math.min(maxScroll, offset + p1)) drawDoc() elseif e == "key" then if p1 == keys.down then local maxScroll = math.max(0, #lines - (h - 3)) offset = math.max(0, math.min(maxScroll, offset + 1)) drawDoc() elseif p1 == keys.up then offset = math.max(0, offset - 1) drawDoc() elseif p1 == keys.enter then return end elseif e == "mouse_click" and p3 == h and p2 >= math.floor(w/2)-9 and p2 <= math.floor(w/2)+9 then return end end end local eulaText = { "MIT License", "", "Copyright (c) 2026 Coralius Realms", "", "Permission is hereby granted, free of charge, to any person obtaining a copy", "of this software and associated documentation files (the 'Software'), to deal", "in the Software without restriction, including without limitation the rights", "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", "copies of the Software, and to permit persons to whom the Software is", "furnished to do so, subject to the following conditions:", "", "The above copyright notice and this permission notice shall be included in all", "copies or substantial portions of the Software.", "", "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", "SOFTWARE." } local function stepReadme() drawHeader("Fetching Documentation") center(h/2, "Downloading System Readme...", colors.yellow) local lines = {} local req = http.get(baseURL .. "common/readme") if req then local content = req.readAll() req.close() for line in content:gmatch("([^\n]*)\n?") do table.insert(lines, line) end else lines = {"[ERROR] Failed to connect to Coralius Realms.", "Press Accept & Continue to bypass..."} end readDocView("System Documentation", lines) end -- ========================================== -- STEP 3: TARGET -- ========================================== local function promptTarget() if not (fs.exists("disk") and fs.isDir("disk")) then return "" end drawHeader("Target Drive") center(h/2 - 4, "=== STORAGE DRIVE DETECTED ===", colors.cyan) center(h/2 - 2, "Where would you like to install GatOS?", colors.white) local box1X, box2X, boxY = math.floor(w/2) - 15, math.floor(w/2) + 2, math.floor(h/2) dr(box1X, boxY, 13, 3, colors.blue) dt(box1X+2, boxY+1, "COMPUTER", colors.white, colors.blue) dr(box2X, boxY, 13, 3, colors.orange) dt(box2X+4, boxY+1, "DISK", colors.white, colors.orange) while true do local e, p1, p2, p3 = os.pullEvent("mouse_click") if p3 >= boxY and p3 <= boxY + 2 then if p2 >= box1X and p2 < box1X + 13 then return "" elseif p2 >= box2X and p2 < box2X + 13 then return "disk/" end end end end -- ========================================== -- MULTI-SELECT WIZARD HELPER -- ========================================== local function doChecklist(title, subtitle, options) local selectedIdx = 1 local function drawList() drawHeader(title) center(3, subtitle, colors.white) local startY = 6 for i, opt in ipairs(options) do local y = startY + ((i-1)*2) local isHover = (i == selectedIdx) local box = opt.selected and "[X]" or "[ ]" term.setCursorPos(math.floor(w/2) - 14, y) term.setTextColor(isHover and colors.cyan or colors.lightGray) term.write(box .. " " .. opt.name) end dr(math.floor(w/2)-6, h-2, 12, 1, colors.lime) dt(math.floor(w/2)-4, h-2, " CONTINUE ", colors.black, colors.lime) center(h, "[UP/DN] Navigate | [SPACE] Toggle", colors.gray) end drawList() while true do local e, p1, p2, p3 = os.pullEvent() if e == "key" then if p1 == keys.up and selectedIdx > 1 then selectedIdx = selectedIdx - 1; drawList() elseif p1 == keys.down and selectedIdx < #options then selectedIdx = selectedIdx + 1; drawList() elseif p1 == keys.space then options[selectedIdx].selected = not options[selectedIdx].selected; drawList() elseif p1 == keys.enter then return end elseif e == "mouse_click" then local startY = 6 for i = 1, #options do if p3 == startY + ((i-1)*2) then options[i].selected = not options[i].selected; selectedIdx = i; drawList() end end if p3 == h-2 and p2 >= math.floor(w/2)-6 and p2 <= math.floor(w/2)+6 then return end end end end -- ============================================================ -- MAIN EXECUTION -- ============================================================ clearScreen() if not http then center(h/2, "ERROR: HTTP API is disabled!", colors.red) return end center(h/2, "Initializing Setup Wizard...", colors.yellow) fetchNetworkData() -- Execute Wizard Steps readDocView("License Agreement", eulaText) stepReadme() local installPath = promptTarget() -- Step 4: Role Selection doChecklist("Software Packages", "Select App Collections to Install:", appCollections) -- Step 5: System Configuration (Security & Preferences) local sysOpts = { { name = "Disable Terminal (Ctrl+T)", selected = false }, { name = "Disable App Store Access", selected = false }, { name = "Enable Lockdown (Hide System Apps)", selected = false }, { name = "Enable Menu Category View", selected = true } } doChecklist("System Configuration", "Configure Policies & Preferences:", sysOpts) -- Build Final Queue local downloadQueue = {} for _, file in ipairs(coreFiles) do table.insert(downloadQueue, { url = baseURL .. file, dest = file }) end for _, collection in ipairs(appCollections) do if collection.selected then for _, app in ipairs(collection.apps) do local targetFiles = appFiles[app] if targetFiles then for _, file in ipairs(targetFiles) do table.insert(downloadQueue, { url = storeURL .. file, dest = "apps/" .. file }) end end end end end -- ========================================== -- STEP 6: DEPLOYMENT -- ========================================== drawHeader("Deploying Cassiopeia Architecture") center(4, installPath == "disk/" and "Target: Removable Floppy Disk" or "Target: Local Internal Storage", colors.orange) local total = #downloadQueue local failed = 0 for i, item in ipairs(downloadQueue) do term.setCursorPos(1, 6) term.clearLine() center(6, "Fetching: " .. item.dest, colors.yellow) local response = http.get(item.url) if response then local content = response.readAll() response.close() local fullPath = installPath .. item.dest local dirPath = fs.getDir(fullPath) if not fs.exists(dirPath) then fs.makeDir(dirPath) end local f = fs.open(fullPath, "w") f.write(content) f.close() else center(9 + failed, "Failed: " .. item.dest, colors.red) failed = failed + 1 end local barWidth = 20 local filled = math.floor((i / total) * barWidth) local bar = "[" .. string.rep("=", filled) .. string.rep(" ", barWidth - filled) .. "]" center(8, bar, colors.lime) center(9, math.floor((i / total) * 100) .. "% Complete", colors.white) end -- Generate Final Menu Config (Exact format requested) local menuConfig = { hideTerminal = sysOpts[1].selected, hideStore = sysOpts[2].selected, lockdownMode = sysOpts[3].selected, useCategories = sysOpts[4].selected } local confDir = installPath .. "sys" if not fs.exists(confDir) then fs.makeDir(confDir) end local cf = fs.open(confDir .. "/menu_config.json", "w") cf.write(textutils.serializeJSON(menuConfig)) cf.close() -- Make Empty Directories local emptyDirs = {"documents", "sys/themes"} for _, d in ipairs(emptyDirs) do if not fs.exists(installPath .. d) then fs.makeDir(installPath .. d) end end term.setCursorPos(1, 6) term.clearLine() term.setCursorPos(1, 8) term.clearLine() term.setCursorPos(1, 9) term.clearLine() if failed == 0 then center(8, "Installation Successful!", colors.cyan) else center(8, "Finished with " .. failed .. " errors.", colors.orange) end center(10, "Press any key to reboot into GatOS...", colors.lightGray) os.pullEvent("key") os.reboot()