I forgot you have to give it write permissions (minified)
This commit is contained in:
@@ -0,0 +1,59 @@
|
|||||||
|
name: Minify all Lua files to minified branch
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
minify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout main branch
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Get latest commit info
|
||||||
|
id: commit
|
||||||
|
run: |
|
||||||
|
COMMIT_MSG=$(git log -1 --pretty=%s)
|
||||||
|
echo "message=${COMMIT_MSG}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install luamin
|
||||||
|
run: npm install -g luamin
|
||||||
|
|
||||||
|
- name: Switch to minified branch
|
||||||
|
run: git checkout minified
|
||||||
|
|
||||||
|
- name: Copy files from main
|
||||||
|
run: |
|
||||||
|
git checkout main -- .
|
||||||
|
git reset HEAD
|
||||||
|
|
||||||
|
- name: Minify all Lua files
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
for file in $(find . -name "*.lua" -not -path "./.git/*"); do
|
||||||
|
luamin -f "$file" > "$file.min" || true
|
||||||
|
# For some stupid fucking reason, luamin returns exit code 1
|
||||||
|
mv "$file.min" "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Commit and push to minified branch
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
git add -A
|
||||||
|
git commit -m "${{ steps.commit.outputs.message }} (minified)"
|
||||||
|
git push origin minified
|
||||||
+1
-691
@@ -1,691 +1 @@
|
|||||||
local packages = {...}
|
Error: no such file. (``)
|
||||||
local command = packages[1]
|
|
||||||
table.remove(packages, 1)
|
|
||||||
local fs = import("filesystem")
|
|
||||||
local component = import("component")
|
|
||||||
local agReg = import("/argentum/registry.cfg")
|
|
||||||
if not command then
|
|
||||||
shell.run("help argentum")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not component.list("internet")() then
|
|
||||||
print("\27[91mThis program requires an internet card to run.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local internet = component.internet
|
|
||||||
local source
|
|
||||||
if table.find(packages, "-s") then
|
|
||||||
source = table.remove(packages, table.find(packages, "-s") + 1)
|
|
||||||
table.remove(packages, table.find(packages, "-s"))
|
|
||||||
print("Using " .. source .. " as package source")
|
|
||||||
elseif table.find(packages, "--source") then
|
|
||||||
source = table.remove(packages, table.find(packages, "--source") + 1)
|
|
||||||
table.remove(packages, table.find(packages, "--source"))
|
|
||||||
print("Using " .. source .. " as package source")
|
|
||||||
else
|
|
||||||
print("Using main registry as package source")
|
|
||||||
end
|
|
||||||
if source and source:sub(1, 1) == "/" and source:sub(-1, -1) ~= "/" then
|
|
||||||
source = source .. "/"
|
|
||||||
end
|
|
||||||
local packageList = table.copy(packages)
|
|
||||||
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
local i = 1
|
|
||||||
|
|
||||||
local function getAgConfig(package, source)
|
|
||||||
source = source or agReg[package]
|
|
||||||
local data, errorMessage = getFile(source .. "argentum.cfg")
|
|
||||||
if not data or data == "" then
|
|
||||||
print("\27[91mCould not fetch Ag config: " .. (errorMessage or "returned nil data"))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local func, errorMessage = load(data, "=argentum.cfg", "bt", {})
|
|
||||||
if not func then
|
|
||||||
print("\27[91mCould not fetch Ag config: " .. errorMessage .. "\nPlease contact the package owner.")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local agcfg
|
|
||||||
local status, errorMessage = pcall(function()
|
|
||||||
agcfg = func()
|
|
||||||
end)
|
|
||||||
if not status then
|
|
||||||
print("\27[91mCould not fetch Ag config: " .. errorMessage .. "\nPlease contact the package owner.")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if not agcfg[package] or not agcfg[package].maindir or not agcfg[package].directories or not agcfg[package].files or not agcfg[package].version then
|
|
||||||
local response = ("\27[91mAg config of " .. package .. " is improperly configured.\nPlease contact the package owner.")
|
|
||||||
end
|
|
||||||
return agcfg
|
|
||||||
end
|
|
||||||
|
|
||||||
local function doChecks(package)
|
|
||||||
if not agReg[package] and not source then
|
|
||||||
print("\27[91mPackage " .. package .. " does not exist.")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if fs.exists("/argentum/store/" .. package) then
|
|
||||||
print("\27[91mPackage " .. package .. " is already installed.")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
agcfg = getAgConfig(package, source)
|
|
||||||
if not agcfg then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if agcfg[package].dependencies then
|
|
||||||
for _, dependency in ipairs(agcfg[package].dependencies) do
|
|
||||||
if not agReg[dependency] and not agcfg[dependency] then
|
|
||||||
local response = read(nil, "\27[91mPackage " .. package .. " requires dependency " .. dependency .. " that does not exist.\n[A - Abort/s - Skip]")
|
|
||||||
if response:lower() ~= "s" then
|
|
||||||
fs.remove("/argentum/store/" .. package)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, dependency in pairs(agcfg[package].dependencies) do
|
|
||||||
print(package .. " depends on " .. dependency)
|
|
||||||
if not table.find(packages, dependency) and doChecks(dependency) then
|
|
||||||
table.insert(packages, table.find(packages, package), dependency)
|
|
||||||
table.insert(packageList, dependency)
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function lpad(str, len, char)
|
|
||||||
str=tostring(str)
|
|
||||||
if char == nil then char = ' ' end
|
|
||||||
return string.rep(char, len - #str) .. str
|
|
||||||
end
|
|
||||||
|
|
||||||
local gpu = component.gpu
|
|
||||||
local width,height = gpu.getResolution()
|
|
||||||
local function progress(package,progress)
|
|
||||||
local info = string.format("%s %s%%",package,lpad(math.floor(progress*100),2))
|
|
||||||
|
|
||||||
info=info..string.rep(" ",width-#info)
|
|
||||||
local progX = math.floor(progress*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 function clearProgress()
|
|
||||||
gpu.setBackground(0x000000)
|
|
||||||
gpu.fill(1,height,width,1," ")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function installPackage(package, overwriteFlag)
|
|
||||||
if not overwriteFlag then
|
|
||||||
overwriteFlag = false
|
|
||||||
end
|
|
||||||
if not overwriteFlag then print("Installing " .. package .. "...") end
|
|
||||||
local agcfg = getAgConfig(package, source)
|
|
||||||
if not agcfg then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local source = source or agReg[package]
|
|
||||||
local packageStore = "V" .. agcfg[package].version
|
|
||||||
if agcfg[package].dependencies then
|
|
||||||
for _, dependency in ipairs(agcfg[package].dependencies) do
|
|
||||||
if not agReg[dependency] and not agcfg[dependency] then
|
|
||||||
local response = read(nil, "\27[91mPackage " .. package .. " requires dependency " .. dependency .. " that does not exist.\n[A - Abort/s - Skip]")
|
|
||||||
if response:lower() ~= "s" then
|
|
||||||
fs.remove("/argentum/store/" .. package)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, dependency in pairs(agcfg[package].dependencies) do
|
|
||||||
if agReg[dependency] or agcfg[dependency] then
|
|
||||||
--installPackage(dependency)
|
|
||||||
packageStore = packageStore .. "\nD" .. dependency
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if agcfg[package].directories then
|
|
||||||
for _, directory in pairs(agcfg[package].directories) do
|
|
||||||
if directory:sub(-1, -1) ~= "/" then
|
|
||||||
directory = directory .. "/"
|
|
||||||
end
|
|
||||||
packageStore = "A" .. directory .. "\n" .. packageStore
|
|
||||||
if not fs.exists(directory) then
|
|
||||||
fs.makeDirectory(directory)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for idx, file in ipairs(agcfg[package].files) do
|
|
||||||
clearProgress()
|
|
||||||
::retry::
|
|
||||||
print(" Downloading " .. file .. "...")
|
|
||||||
progress(package,idx/#agcfg[package].files)
|
|
||||||
local data, errorMessage = getFile(source .. agcfg[package].maindir .. file)
|
|
||||||
if not data then
|
|
||||||
clearProgress()
|
|
||||||
local response = read(nil, "\27[91mCould not fetch " .. file .. ": " .. errorMessage .. "\n\27[0m[a - Abort/R - Retry/s - Skip]")
|
|
||||||
if response:lower() == "a" then
|
|
||||||
fs.remove("/argentum/store/" .. package)
|
|
||||||
return false
|
|
||||||
elseif response:lower() == "s" then
|
|
||||||
goto skip
|
|
||||||
else
|
|
||||||
goto retry
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if fs.exists(file) and not overwriteFlag then
|
|
||||||
if not fs.exists("/argentum/store/" .. package .. "/files/" .. file:match("(.*/)")) then
|
|
||||||
fs.makeDirectory("/argentum/store/" .. package .. "/files/" .. file:match("(.*/)"))
|
|
||||||
end
|
|
||||||
fs.copy(file, "/argentum/store/" .. package .. "/files/" .. file)
|
|
||||||
packageStore = packageStore .. "\nM" .. file
|
|
||||||
else
|
|
||||||
packageStore = packageStore .. "\nA" .. file
|
|
||||||
end
|
|
||||||
local handle = fs.open(file, "w")
|
|
||||||
handle:write(data)
|
|
||||||
handle:close()
|
|
||||||
::skip::
|
|
||||||
end
|
|
||||||
clearProgress()
|
|
||||||
fs.makeDirectory("/argentum/store/" .. package)
|
|
||||||
local handle = fs.open("/argentum/store/" .. package .. "/package.cfg", "w")
|
|
||||||
handle:write(packageStore)
|
|
||||||
handle:close()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function removePackage(package)
|
|
||||||
print("Removing " .. package .. "...")
|
|
||||||
if not fs.exists("/argentum/store/" .. package .. "/package.cfg") then
|
|
||||||
print("\27[91mLocal Ag config of " .. package .. " does not exist.")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local handle, data, tmpdata = fs.open("/argentum/store/" .. package .. "/package.cfg", "r"), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
handle:close()
|
|
||||||
data = data .. "\n"
|
|
||||||
local idx = 0
|
|
||||||
for line in data:gmatch("(.-)\n") do
|
|
||||||
idx=idx+1
|
|
||||||
if line:sub(1, 1) == "A" then
|
|
||||||
clearProgress()
|
|
||||||
::retry::
|
|
||||||
print(" Removing " .. line:sub(2) .. "...")
|
|
||||||
if line:sub(-1, -1) == "/" and fs.list(line:sub(2))[1] then
|
|
||||||
print(" There are still files in " .. line:sub(2) .. ". Skipping.")
|
|
||||||
else
|
|
||||||
progress(package,idx/select(2,data:gsub("\n","\n")))
|
|
||||||
local result, errorMessage = fs.remove(line:sub(2))
|
|
||||||
if not result then
|
|
||||||
clearProgress()
|
|
||||||
local response = read(nil, "\27[91mFailed to remove " .. line:sub(2) .. ": " .. errorMessage .. "\n\27[0m[a - Abort/r - Retry/S - Skip]")
|
|
||||||
if response:lower() == "a" then
|
|
||||||
return false
|
|
||||||
elseif response:lower() == "r" then
|
|
||||||
goto retry
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif line:sub(1, 1) == "M" then
|
|
||||||
clearProgress()
|
|
||||||
::retry::
|
|
||||||
print(" Reverting " .. line:sub(2) .. "...")
|
|
||||||
progress(package,idx/select(2,data:gsub("\n","\n")))
|
|
||||||
local handle, data, tmpdata = fs.open("/argentum/store/" .. package .. "/files/" .. line:sub(2), "r"), "", nil
|
|
||||||
if not handle then
|
|
||||||
clearProgress()
|
|
||||||
local response = read(nil, "\27[91mFailed to revert " .. line:sub(2) .. ": " .. data .. "\n\27[0m[a - Abort/R - Retry/s - Skip]") -- this is pretty stupid but i think the error message would get pushed to data
|
|
||||||
if response:lower() == "a" then
|
|
||||||
return false
|
|
||||||
elseif response:lower() == "s" then
|
|
||||||
goto skip
|
|
||||||
else
|
|
||||||
goto retry
|
|
||||||
end
|
|
||||||
end
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
handle:close()
|
|
||||||
local handle = fs.open(line:sub(2), "w")
|
|
||||||
handle:write(data)
|
|
||||||
handle:close()
|
|
||||||
::skip::
|
|
||||||
end
|
|
||||||
end
|
|
||||||
clearProgress()
|
|
||||||
fs.remove("/argentum/store/" .. package .. "/")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function updatePackage(package)
|
|
||||||
print("Updating " .. package .. "...")
|
|
||||||
local agcfg = getAgConfig(package, source)
|
|
||||||
if not agcfg then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local handle, data, tmpdata = fs.open("/argentum/store/" .. package .. "/package.cfg", "r"), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
handle:close()
|
|
||||||
local oldFiles = {}
|
|
||||||
for line in (data .. "\n"):gmatch("(.-)\n") do
|
|
||||||
if line:sub(1, 1) == "A" or line:sub(1, 1) == "M" then
|
|
||||||
if agcfg[package].directories then
|
|
||||||
if not table.find(agcfg[package].files, line:sub(2)) and not table.find(agcfg[package].directories, line:sub(2, -2)) then
|
|
||||||
table.insert(oldFiles, line:sub(2))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if not table.find(agcfg[package].files, line:sub(2)) then
|
|
||||||
table.insert(oldFiles, line:sub(2))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, oldFile in pairs(oldFiles) do
|
|
||||||
print(" Removing " .. oldFile .. "...")
|
|
||||||
end
|
|
||||||
return installPackage(package, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
local fails = {}
|
|
||||||
if command == "install" then
|
|
||||||
if not packages or not packages[1] then
|
|
||||||
print("Please specify packages to install.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
print("Fetching Ag registry...")
|
|
||||||
local newRegistry, errorMessage = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/argentum/registry.cfg")
|
|
||||||
if newRegistry then
|
|
||||||
local handle = fs.open("/argentum/registry.cfg", "w")
|
|
||||||
handle:write(newRegistry)
|
|
||||||
handle:close()
|
|
||||||
else
|
|
||||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
|
||||||
end
|
|
||||||
agReg = import("/argentum/registry.cfg")
|
|
||||||
while true do
|
|
||||||
if not doChecks(packages[i]) then
|
|
||||||
table.insert(fails, packages[i])
|
|
||||||
table.remove(packageList, table.find(packageList, packages[i]))
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
if i > #packages then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local answer
|
|
||||||
if #fails == 0 then
|
|
||||||
print("Packages that will be installed: " .. table.concat(packageList, ", "))
|
|
||||||
if read(nil, "Would you like to proceed? [Y/n] "):lower() == "n" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
elseif #packageList == 0 then
|
|
||||||
print("None of the packages can be installed.")
|
|
||||||
return
|
|
||||||
else
|
|
||||||
print("Some packages cannot be installed.")
|
|
||||||
print("Packages that will be installed: " .. table.concat(packageList, ", "))
|
|
||||||
print("Packages that cannot be installed: " .. table.concat(fails, ", "))
|
|
||||||
if read(nil, "Would you like to proceed? [y/N] "):lower() ~= "y" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, failedPackage in pairs(fails) do
|
|
||||||
table.remove(packages, table.find(packages, failedPackage))
|
|
||||||
end
|
|
||||||
fails = {}
|
|
||||||
for _, package in ipairs(packages) do
|
|
||||||
if not installPackage(package) then
|
|
||||||
table.insert(fails, package)
|
|
||||||
table.remove(packageList, table.find(packageList, package))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #fails == 0 then
|
|
||||||
print("Installation completed successfully.")
|
|
||||||
print("Packages installed: " .. table.concat(packageList, ", "))
|
|
||||||
elseif #packageList == 0 then
|
|
||||||
print("All packages failed to install.")
|
|
||||||
print("Packages that could not be installed: " .. table.concat(fails, ", "))
|
|
||||||
else
|
|
||||||
print("Some packages failed to install.")
|
|
||||||
print("Packages installed: " .. table.concat(packageList, ", "))
|
|
||||||
print("Packages that could not be installed: " .. table.concat(fails, ", "))
|
|
||||||
end
|
|
||||||
elseif command == "remove" then
|
|
||||||
if not packages or not packages[1] then
|
|
||||||
print("Please specify packages to remove.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
while true do
|
|
||||||
if not fs.exists("/argentum/store/" .. packages[i]) then
|
|
||||||
print("\27[91mPackage " .. packages[i] .. " is not installed.")
|
|
||||||
table.insert(fails, packages[i])
|
|
||||||
table.remove(packageList, table.find(packageList, packages[i]))
|
|
||||||
table.remove(packages, table.find(packages, packages[i]))
|
|
||||||
i = i - 1
|
|
||||||
elseif packages[i] == "halyde" then -- yes, this stuff is hard-coded.
|
|
||||||
print("\27[91mFor obvious reasons, you can't uninstall Halyde.")
|
|
||||||
table.insert(fails, packages[i])
|
|
||||||
table.remove(packageList, table.find(packageList, packages[i]))
|
|
||||||
table.remove(packages, table.find(packages, packages[i]))
|
|
||||||
i = i - 1
|
|
||||||
elseif packages[i] == "argentum" then
|
|
||||||
print("\27[91mFor obvious reasons, you can't uninstall Argentum.")
|
|
||||||
table.insert(fails, packages[i])
|
|
||||||
table.remove(packageList, table.find(packageList, packages[i]))
|
|
||||||
table.remove(packages, table.find(packages, packages[i]))
|
|
||||||
i = i - 1
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
if i > #packages then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- do dependency checks
|
|
||||||
local packagesInstalled = fs.list("/argentum/store")
|
|
||||||
for _, currentPackage in pairs(packagesInstalled) do
|
|
||||||
if currentPackage:sub(-1, -1) == "/" and fs.exists("/argentum/store/" .. currentPackage .. "package.cfg") then
|
|
||||||
local handle, data, tmpdata = fs.open("/argentum/store/" .. currentPackage .. "package.cfg", "r"), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
handle:close()
|
|
||||||
for line in (data.."\n"):gmatch("(.-)\n") do
|
|
||||||
for i = 1, #packages do
|
|
||||||
if line == "D" .. packages[i] then
|
|
||||||
print(packages[i] .. " depends on " .. currentPackage:sub(1, -2))
|
|
||||||
if not table.find(packages, currentPackage:sub(1, -2)) then
|
|
||||||
table.insert(packages, table.find(packages, packages[i]), currentPackage:sub(1, -2))
|
|
||||||
table.insert(packageList, currentPackage:sub(1, -2))
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local answer
|
|
||||||
if #fails == 0 then
|
|
||||||
print("Packages that will be removed: " .. table.concat(packageList, ", "))
|
|
||||||
if read(nil, "Would you like to proceed? [Y/n] "):lower() == "n" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
elseif #packageList == 0 then
|
|
||||||
print("None of the packages can be removed.")
|
|
||||||
return
|
|
||||||
else
|
|
||||||
print("Some packages cannot be removed.")
|
|
||||||
print("Packages that will be removed: " .. table.concat(packageList, ", "))
|
|
||||||
print("Packages that cannot be removed: " .. table.concat(fails, ", "))
|
|
||||||
if read(nil, "Would you like to proceed? [y/N] "):lower() ~= "y" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, failedPackage in pairs(fails) do
|
|
||||||
table.remove(packages, table.find(packages, failedPackage))
|
|
||||||
end
|
|
||||||
fails = {}
|
|
||||||
for _, package in ipairs(packages) do
|
|
||||||
if not removePackage(package) then
|
|
||||||
table.insert(fails, package)
|
|
||||||
table.remove(packageList, table.find(packageList, package))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #fails == 0 then
|
|
||||||
print("Removal completed successfully.")
|
|
||||||
print("Packages removed: " .. table.concat(packageList, ", "))
|
|
||||||
elseif #packageList == 0 then
|
|
||||||
print("All packages failed to be removed.")
|
|
||||||
print("Packages that could not be removed: " .. table.concat(fails, ", "))
|
|
||||||
else
|
|
||||||
print("Some packages failed to be removed.")
|
|
||||||
print("Packages removed: " .. table.concat(packageList, ", "))
|
|
||||||
print("Packages that could not be removed: " .. table.concat(fails, ", "))
|
|
||||||
end
|
|
||||||
elseif command == "update" then
|
|
||||||
print("Fetching Ag registry...")
|
|
||||||
local newRegistry, errorMessage = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/argentum/registry.cfg")
|
|
||||||
if newRegistry then
|
|
||||||
local handle = fs.open("/argentum/registry.cfg", "w")
|
|
||||||
handle:write(newRegistry)
|
|
||||||
handle:close()
|
|
||||||
else
|
|
||||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
|
||||||
end
|
|
||||||
agReg = import("/argentum/registry.cfg")
|
|
||||||
if not packages[1] then
|
|
||||||
local packagesInstalled = fs.list("/argentum/store/")
|
|
||||||
for _, currentPackage in pairs(packagesInstalled) do
|
|
||||||
if currentPackage:sub(-1, -1) == "/" and fs.exists("/argentum/store/" .. currentPackage .. "package.cfg") then
|
|
||||||
table.insert(packages, currentPackage:sub(1, -2))
|
|
||||||
table.insert(packageList, currentPackage:sub(1, -2))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while true do
|
|
||||||
local nonexistent = false -- I couldn't figure out a better way to do this, so I have to use a flag if the package doesn't exist
|
|
||||||
if not fs.exists("/argentum/store/" .. packages[i]) then
|
|
||||||
print("\27[91mPackage " .. packages[i] .. " is not installed.")
|
|
||||||
table.insert(fails, packages[i])
|
|
||||||
table.remove(packageList, table.find(packageList, packages[i]))
|
|
||||||
table.remove(packages, table.find(packages, packages[i]))
|
|
||||||
i = i - 1
|
|
||||||
nonexistent = true
|
|
||||||
end
|
|
||||||
if not nonexistent then
|
|
||||||
-- Check if up to date
|
|
||||||
local agcfg = getAgConfig(packages[i], source)
|
|
||||||
if not agcfg then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local handle, data, tmpdata = fs.open("/argentum/store/" .. packages[i] .. "/package.cfg", "r"), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
handle:close()
|
|
||||||
local version = "0.0.0"
|
|
||||||
for line in (data.."\n"):gmatch("(.-)\n") do
|
|
||||||
if line:sub(1, 1) == "V" then
|
|
||||||
version = line:sub(2)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if agcfg[packages[i]].version == version then
|
|
||||||
table.remove(packageList, table.find(packageList, packages[i]))
|
|
||||||
table.remove(packages, table.find(packages, packages[i]))
|
|
||||||
i = i - 1
|
|
||||||
else
|
|
||||||
print(packages[i].." is out of date [\x1b[93m"..version.."\x1b[39m < \x1b[92m"..agcfg[packages[i]].version.."\x1b[39m]")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
if i > #packages then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local answer
|
|
||||||
if #packageList == 0 then
|
|
||||||
if #fails == 0 then
|
|
||||||
print("All packages are up to date.")
|
|
||||||
return
|
|
||||||
else
|
|
||||||
print("None of the packages can be updated.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
elseif #fails == 0 then
|
|
||||||
print("Packages that will be updated: " .. table.concat(packageList, ", "))
|
|
||||||
if read(nil, "Would you like to proceed? [Y/n] "):lower() == "n" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
else
|
|
||||||
print("Some packages cannot be updated.")
|
|
||||||
print("Packages that will be updated: " .. table.concat(packageList, ", "))
|
|
||||||
print("Packages that cannot be updated: " .. table.concat(fails, ", "))
|
|
||||||
if read(nil, "Would you like to proceed? [y/N] "):lower() ~= "y" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, failedPackage in pairs(fails) do
|
|
||||||
table.remove(packages, table.find(packages, failedPackage))
|
|
||||||
end
|
|
||||||
fails = {}
|
|
||||||
for _, package in pairs(packages) do
|
|
||||||
-- Previous up-to-date check
|
|
||||||
|
|
||||||
--local agcfg = getAgConfig(package, source)
|
|
||||||
--if not agcfg then
|
|
||||||
-- return false
|
|
||||||
--end
|
|
||||||
--local handle, data, tmpdata = fs.open("/argentum/store/" .. package .. "/package.cfg", "r"), "", nil
|
|
||||||
--repeat
|
|
||||||
-- tmpdata = handle:read(math.huge)
|
|
||||||
-- data = data .. (tmpdata or "")
|
|
||||||
--until not tmpdata
|
|
||||||
--handle:close()
|
|
||||||
--local version = "0.0.0"
|
|
||||||
--for line in (data.."\n"):gmatch("(.-)\n") do
|
|
||||||
-- if line:sub(1, 1) == "V" then
|
|
||||||
-- version = line:sub(2)
|
|
||||||
-- break
|
|
||||||
-- end
|
|
||||||
--end
|
|
||||||
--if agcfg[package].version == version then
|
|
||||||
-- print(package .. " is up to date")
|
|
||||||
-- goto skip
|
|
||||||
--end
|
|
||||||
if not updatePackage(package) then
|
|
||||||
table.insert(fails, packages[i])
|
|
||||||
table.remove(packageList, table.find(packageList, packages[i]))
|
|
||||||
table.remove(packages, table.find(packages, packages[i]))
|
|
||||||
goto skip
|
|
||||||
end
|
|
||||||
::skip::
|
|
||||||
end
|
|
||||||
if #fails == 0 then
|
|
||||||
print("Update completed successfully.")
|
|
||||||
print("Packages updated: " .. table.concat(packageList, ", "))
|
|
||||||
elseif #packageList == 0 then
|
|
||||||
print("All packages failed to update.")
|
|
||||||
print("Packages that could not update: " .. table.concat(fails, ", "))
|
|
||||||
else
|
|
||||||
print("Some packages failed to update.")
|
|
||||||
print("Packages updated: " .. table.concat(packageList, ", "))
|
|
||||||
print("Packages that could not update: " .. table.concat(fails, ", "))
|
|
||||||
end
|
|
||||||
elseif command == "info" then
|
|
||||||
if not packages[1] then
|
|
||||||
print("Please specify a package to show information about.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
print("Fetching Ag registry...")
|
|
||||||
local newRegistry, errorMessage = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/argentum/registry.cfg")
|
|
||||||
if newRegistry then
|
|
||||||
local handle = fs.open("/argentum/registry.cfg", "w")
|
|
||||||
handle:write(newRegistry)
|
|
||||||
handle:close()
|
|
||||||
else
|
|
||||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
|
||||||
end
|
|
||||||
agReg = import("/argentum/registry.cfg")
|
|
||||||
if not agReg[packages[1]] and not source then
|
|
||||||
print("\27[91mPackage " .. packages[1] .. " does not exist.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local agcfg = getAgConfig(packages[1], source)
|
|
||||||
if not agcfg then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
print("\27[93m" .. packages[1] .. "\27[0m v" .. agcfg[packages[1]].version .. "\n " .. (agcfg[packages[1]].description or "No description."):gsub("\n", " \n"))
|
|
||||||
elseif command == "search" then
|
|
||||||
if not packages[1] then
|
|
||||||
print("Please specify a search term.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
print("Fetching Ag registry...")
|
|
||||||
local newRegistry, errorMessage = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/argentum/registry.cfg")
|
|
||||||
if newRegistry then
|
|
||||||
local handle = fs.open("/argentum/registry.cfg", "w")
|
|
||||||
handle:write(newRegistry)
|
|
||||||
handle:close()
|
|
||||||
else
|
|
||||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
|
||||||
end
|
|
||||||
agReg = import("/argentum/registry.cfg")
|
|
||||||
local searchResults = {}
|
|
||||||
for packageName, _ in pairs(agReg) do
|
|
||||||
if packageName:find(packages[1], 1, true) then
|
|
||||||
table.insert(searchResults, packageName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not searchResults[1] then
|
|
||||||
print("No search results found for " .. packages[1] .. ".")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
table.sort(searchResults)
|
|
||||||
print("Search results: \n " .. table.concat(searchResults, "\n "))
|
|
||||||
elseif command == "list" then
|
|
||||||
print("Fetching Ag registry...")
|
|
||||||
local newRegistry, errorMessage = getFile("https://raw.githubusercontent.com/Team-Cerulean-Blue/Halyde/refs/heads/main/argentum/registry.cfg")
|
|
||||||
if newRegistry then
|
|
||||||
local handle = fs.open("/argentum/registry.cfg", "w")
|
|
||||||
handle:write(newRegistry)
|
|
||||||
handle:close()
|
|
||||||
else
|
|
||||||
print("\27[91mFailed to fetch Ag registry: " .. (errorMessage or "returned nil"))
|
|
||||||
end
|
|
||||||
agReg = import("/argentum/registry.cfg")
|
|
||||||
local sortedPackages = {}
|
|
||||||
for packageName, _ in pairs(agReg) do
|
|
||||||
table.insert(sortedPackages, packageName)
|
|
||||||
end
|
|
||||||
table.sort(sortedPackages)
|
|
||||||
print("List of available Ag packages: \n " .. table.concat(sortedPackages, "\n "))
|
|
||||||
else
|
|
||||||
shell.run("help ag")
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-60
@@ -1,60 +1 @@
|
|||||||
local component = import("component")
|
Error: no such file. (``)
|
||||||
local computer = import("computer")
|
|
||||||
local args = {...}
|
|
||||||
|
|
||||||
local force = false
|
|
||||||
|
|
||||||
local forceArgIdx = table.find(args,"-f") or table.find(args,"--force")
|
|
||||||
if forceArgIdx then
|
|
||||||
table.remove(args,forceArgIdx)
|
|
||||||
force = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getComponentID(str)
|
|
||||||
local function fromSlot(slot)
|
|
||||||
for i,v in component.list() do
|
|
||||||
if component.slot(i)==slot then
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if str=="hdd1" or str=="#1" then return fromSlot(5) end
|
|
||||||
if str=="hdd2" or str=="#2" then return fromSlot(6) end
|
|
||||||
if str=="floppy" or str=="#3" then return fromSlot(7) end
|
|
||||||
|
|
||||||
if #str<3 then return nil,"Abbreviated ID must atleast have 3 characters" end
|
|
||||||
return component.get(str)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function fileExists(compID,file)
|
|
||||||
return component.invoke(compID,"exists",file) and not component.invoke(compID,"isDirectory",file)
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(args[1])=="string" then
|
|
||||||
local compID,err = getComponentID(args[1])
|
|
||||||
if not compID then
|
|
||||||
print("\x1b[91mCould not get component ID from '"..args[1].."'.")
|
|
||||||
if type(err)=="string" then print("\x1b[91m"..err) end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not force then
|
|
||||||
if componentlib.additions[compID] then
|
|
||||||
return print("\x1b[91mThis component is virtual and cannot be booted from directly.\nID: "..compID)
|
|
||||||
end
|
|
||||||
local type = component.type(compID)
|
|
||||||
if type~="filesystem" and type~="drive" then
|
|
||||||
return print("\x1b[91mThis component is not a storage medium.\nID: "..compID)
|
|
||||||
end
|
|
||||||
if type=="filesystem" and not fileExists(compID,"/init.lua") then
|
|
||||||
return print("\x1b[91mThis storage medium doesn't have an \"init.lua\" file.\nID: "..compID)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
computer.setBootAddress(compID)
|
|
||||||
if computer.getBootAddress()~=compID then
|
|
||||||
return print("\x1b[91mFailed to set the boot address.")
|
|
||||||
end
|
|
||||||
computer.shutdown(true)
|
|
||||||
else
|
|
||||||
shell.run("help boot")
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-20
@@ -1,20 +1 @@
|
|||||||
local files = {...}
|
Error: no such file. (``)
|
||||||
local fs = import("filesystem")
|
|
||||||
if not files or not files[1] then
|
|
||||||
shell.run("help cat")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
for _, file in ipairs(files) do
|
|
||||||
if file:sub(1, 1) ~= "/" then
|
|
||||||
file = fs.concat(shell.workingDirectory, file)
|
|
||||||
end
|
|
||||||
if not fs.exists(file) then
|
|
||||||
print("\27[91mFile does not exist.")
|
|
||||||
end
|
|
||||||
local handle = fs.open(file, "r")
|
|
||||||
local data
|
|
||||||
repeat
|
|
||||||
data = handle:read(math.huge or math.maxinteger)
|
|
||||||
termlib.write(data)
|
|
||||||
until not data
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-14
@@ -1,14 +1 @@
|
|||||||
local directory = ...
|
Error: no such file. (``)
|
||||||
local fs = import("filesystem")
|
|
||||||
|
|
||||||
if not directory then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if directory:sub(1, 1) ~= "/" then
|
|
||||||
directory = fs.concat(shell.workingDirectory, directory)
|
|
||||||
end
|
|
||||||
if fs.exists(directory) and fs.isDirectory(directory) then
|
|
||||||
shell.workingDirectory = fs.canonical(directory)
|
|
||||||
else
|
|
||||||
print("\27[91mNo such directory.")
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
clear()
|
Error: no such file. (``)
|
||||||
-- truly so much going on here
|
|
||||||
|
|||||||
+1
-26
@@ -1,26 +1 @@
|
|||||||
local fromFile, toFile = ...
|
Error: no such file. (``)
|
||||||
local fs = import("filesystem")
|
|
||||||
|
|
||||||
if not fromFile or not toFile then
|
|
||||||
shell.run("help cp")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if fromFile:sub(1, 1) ~= "/" then
|
|
||||||
fromFile = fs.concat(shell.workingDirectory, fromFile)
|
|
||||||
end
|
|
||||||
if toFile:sub(1, 1) ~= "/" then
|
|
||||||
toFile = fs.concat(shell.workingDirectory, toFile)
|
|
||||||
end
|
|
||||||
if fromFile == toFile then
|
|
||||||
print("\27[91mSource and destination are the same.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not fs.exists(fromFile) then
|
|
||||||
print("\27[91mSource file does not exist.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if fs.exists(toFile) and not (table.find({...}, "-o") or table.find({...}, "--overwrite")) then
|
|
||||||
print("\27[91mDestination file already exists. Run this command again with -o to overwrite it.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
fs.copy(fromFile, toFile)
|
|
||||||
|
|||||||
@@ -1,57 +1 @@
|
|||||||
local url = ...
|
Error: no such file. (``)
|
||||||
|
|
||||||
local component = import("component")
|
|
||||||
local fs = import("filesystem")
|
|
||||||
|
|
||||||
if not component.list("internet")() then
|
|
||||||
print("\27[91mThis program requires an internet card to run.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not url then
|
|
||||||
print("Please enter a URL to download from.")
|
|
||||||
shell.run("help download")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if url:sub(-1, -1) == "/" then
|
|
||||||
url = url:sub(1, -2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local internet = component.internet
|
|
||||||
|
|
||||||
local request, data, tmpdata = nil, "", nil
|
|
||||||
local status, errorMessage = pcall(function()
|
|
||||||
request = internet.request(url)
|
|
||||||
request:finishConnect()
|
|
||||||
end)
|
|
||||||
if not status then
|
|
||||||
print("\27[91mDownload failed: " .. errorMessage)
|
|
||||||
end
|
|
||||||
local responseCode = request:response()
|
|
||||||
if responseCode and responseCode ~= 200 then
|
|
||||||
print("\27[91mDownload failed: " .. tostring(responseCode))
|
|
||||||
end
|
|
||||||
repeat
|
|
||||||
tmpdata = request.read(math.huge)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
local saveLocation
|
|
||||||
local saveLocationOK = false
|
|
||||||
repeat
|
|
||||||
saveLocation = read(nil, "File save location: ", fs.concat(shell.workingDirectory, url:match("/([^/]+)$")))
|
|
||||||
if fs.isDirectory(saveLocation) then
|
|
||||||
print("\27[91mThe specified location is a directory.")
|
|
||||||
elseif fs.exists(saveLocation) then
|
|
||||||
local answer = read(nil, "\27[91mThere is already a file at the specified directory. Overwrite it? [Y/n]")
|
|
||||||
if answer:lower() ~= "n" then
|
|
||||||
saveLocationOK = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
saveLocationOK = true
|
|
||||||
end
|
|
||||||
until saveLocationOK
|
|
||||||
local handle = fs.open(saveLocation, "w")
|
|
||||||
handle:write(data)
|
|
||||||
handle:close()
|
|
||||||
print("File downloaded successfully.")
|
|
||||||
|
|||||||
@@ -1,7 +1 @@
|
|||||||
local args = {...}
|
Error: no such file. (``)
|
||||||
local concatText = args[1]
|
|
||||||
table.remove(args, 1)
|
|
||||||
for _, item in pairs(args) do
|
|
||||||
concatText = concatText .. " " .. item
|
|
||||||
end
|
|
||||||
print(concatText)
|
|
||||||
|
|||||||
+1
-351
@@ -1,351 +1 @@
|
|||||||
local file = ...
|
Error: no such file. (``)
|
||||||
local fs = import("filesystem")
|
|
||||||
local event = import("event")
|
|
||||||
local component = import("component")
|
|
||||||
local unicode = import("unicode")
|
|
||||||
local gpu = component.gpu
|
|
||||||
local width, height = gpu.getResolution()
|
|
||||||
local scrollPosX, scrollPosY = 1, 1
|
|
||||||
local cursorPosX, cursorPosY = 1, 1
|
|
||||||
local cursorWhite = true
|
|
||||||
local changesMade = false
|
|
||||||
local renderBuffer = gpu.allocateBuffer()
|
|
||||||
local scrollSpeed = 5
|
|
||||||
local tab = " "
|
|
||||||
--local ocelot = component.ocelot
|
|
||||||
|
|
||||||
local function rawset(x, y, text)
|
|
||||||
termlib.cursorPosX = x
|
|
||||||
termlib.cursorPosY = y
|
|
||||||
termlib.write(text, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
local filestring, filepath, handle, data, tmpdata
|
|
||||||
if file then
|
|
||||||
if file:sub(1, 1) == "/" then
|
|
||||||
filepath = file
|
|
||||||
else
|
|
||||||
filepath = shell.workingDirectory .. file
|
|
||||||
end
|
|
||||||
handle, data, tmpdata = fs.open(filepath, "r"), "", nil
|
|
||||||
if fs.exists(filepath) then
|
|
||||||
filestring = filepath
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
tmpdata = {}
|
|
||||||
if data:gmatch("(.-)\n")() then
|
|
||||||
for line in data:gmatch("(.-)\n") do
|
|
||||||
local newLine = line:gsub("\r", "") -- this took me SO LONG TO FIGURE OUT AAAAAAAA I HATE CRLF I HATE CRLF I HATE CRLF
|
|
||||||
table.insert(tmpdata, newLine)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
tmpdata = {data}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
filepath = shell.workingDirectory .. file
|
|
||||||
filestring = "[NEW FILE]"
|
|
||||||
tmpdata = {""}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
filepath = ""
|
|
||||||
filestring = "[NEW FILE]"
|
|
||||||
tmpdata = {""}
|
|
||||||
end
|
|
||||||
local function render()
|
|
||||||
gpu.setActiveBuffer(renderBuffer)
|
|
||||||
clear()
|
|
||||||
--ocelot.log(tostring(scrollPosY))
|
|
||||||
local realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2)
|
|
||||||
if realCursorX < 1 then
|
|
||||||
scrollPosX = scrollPosX + realCursorX - 1
|
|
||||||
cursorPosX = 1
|
|
||||||
realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2)
|
|
||||||
end
|
|
||||||
for i = scrollPosY, height + scrollPosY - 3 do
|
|
||||||
gpu.set(1, i - scrollPosY + 1, (tmpdata[i] or ""):sub(scrollPosX))
|
|
||||||
end
|
|
||||||
rawset(1, height - 1, "\27[107m\27[30m" .. filestring .. string.rep(" ", width))
|
|
||||||
rawset(1, height, "\27[107m\27[30m^X\27[0m Exit \27[107m\27[30m^S\27[0m Save" .. string.rep(" ", width))
|
|
||||||
local char = gpu.get(realCursorX, cursorPosY)
|
|
||||||
if cursorWhite then
|
|
||||||
gpu.setForeground(0)
|
|
||||||
gpu.setBackground(0xFFFFFF)
|
|
||||||
end
|
|
||||||
gpu.set(realCursorX, cursorPosY, char)
|
|
||||||
gpu.bitblt()
|
|
||||||
gpu.setActiveBuffer(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
local renderFlag, cursorRenderFlag = false, false
|
|
||||||
|
|
||||||
local function scrollUp()
|
|
||||||
cursorPosY = cursorPosY - 1
|
|
||||||
cursorRenderFlag = true
|
|
||||||
cursorWhite = true
|
|
||||||
if cursorPosY < 1 then
|
|
||||||
renderFlag = true
|
|
||||||
scrollPosY = scrollPosY - 1
|
|
||||||
cursorPosY = 1
|
|
||||||
end
|
|
||||||
if scrollPosY < 1 then
|
|
||||||
renderFlag = false
|
|
||||||
scrollPosY = 1
|
|
||||||
end
|
|
||||||
if math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) < 1 then
|
|
||||||
renderFlag = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scrollDown()
|
|
||||||
cursorPosY = cursorPosY + 1
|
|
||||||
cursorRenderFlag = true
|
|
||||||
cursorWhite = true
|
|
||||||
if cursorPosY + scrollPosY - 1 > #tmpdata then
|
|
||||||
renderFlag = false
|
|
||||||
cursorPosY = #tmpdata - scrollPosY + 1
|
|
||||||
end
|
|
||||||
if cursorPosY > height - 2 then
|
|
||||||
renderFlag = true
|
|
||||||
scrollPosY = scrollPosY + 1
|
|
||||||
cursorPosY = height - 2
|
|
||||||
end
|
|
||||||
if math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) < 1 then
|
|
||||||
renderFlag = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scrollLeft()
|
|
||||||
cursorRenderFlag = true
|
|
||||||
cursorWhite = true
|
|
||||||
if cursorPosX > 1 then
|
|
||||||
if cursorPosX <= unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2 then
|
|
||||||
cursorPosX = cursorPosX - 1
|
|
||||||
elseif unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 1 > 1 then
|
|
||||||
cursorPosX = unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 1
|
|
||||||
end
|
|
||||||
elseif scrollPosX > 1 then
|
|
||||||
scrollPosX = scrollPosX - 1
|
|
||||||
renderFlag = true
|
|
||||||
end
|
|
||||||
if math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) < 1 then
|
|
||||||
renderFlag = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scrollRight()
|
|
||||||
cursorRenderFlag = true
|
|
||||||
cursorWhite = true
|
|
||||||
if cursorPosX <= unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 1 then
|
|
||||||
cursorPosX = cursorPosX + 1
|
|
||||||
end
|
|
||||||
if cursorPosX > width then
|
|
||||||
cursorPosX = width
|
|
||||||
scrollPosX = scrollPosX + 1
|
|
||||||
renderFlag = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function processEvent(args)
|
|
||||||
renderFlag, cursorRenderFlag = false, false
|
|
||||||
if args[1] == "key_down" then
|
|
||||||
local keycode = args[4]
|
|
||||||
local key = keyboard.keys[keycode]
|
|
||||||
if keyboard.ctrlDown then
|
|
||||||
return false, false, key
|
|
||||||
end
|
|
||||||
if key == "down" and cursorPosY < #tmpdata then
|
|
||||||
scrollDown()
|
|
||||||
end
|
|
||||||
if key == "up" then
|
|
||||||
scrollUp()
|
|
||||||
end
|
|
||||||
if key == "left" then
|
|
||||||
scrollLeft()
|
|
||||||
end
|
|
||||||
if key == "right" then
|
|
||||||
scrollRight()
|
|
||||||
end
|
|
||||||
if key == "enter" then
|
|
||||||
changesMade = true
|
|
||||||
renderFlag = true
|
|
||||||
cursorWhite = true
|
|
||||||
table.insert(tmpdata, cursorPosY + 1, tmpdata[cursorPosY + scrollPosY - 1]:sub(cursorPosX))
|
|
||||||
tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1]:sub(1, cursorPosX - 1)
|
|
||||||
cursorPosX = 1
|
|
||||||
cursorPosY = cursorPosY + 1
|
|
||||||
scrollPosX = 1
|
|
||||||
if cursorPosY > height - 2 then
|
|
||||||
scrollPosY = scrollPosY + 1
|
|
||||||
cursorPosY = height - 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if key == "back" then
|
|
||||||
changesMade = true
|
|
||||||
cursorRenderFlag = true
|
|
||||||
cursorWhite = true
|
|
||||||
if cursorPosX == 1 and cursorPosY + scrollPosY - 1 > 1 then
|
|
||||||
cursorPosY = cursorPosY - 1
|
|
||||||
if cursorPosY < 1 then
|
|
||||||
scrollPosY = scrollPosY - 1
|
|
||||||
cursorPosY = 1
|
|
||||||
end
|
|
||||||
if scrollPosY < 1 then
|
|
||||||
scrollPosY = 1
|
|
||||||
end
|
|
||||||
cursorPosX = unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2
|
|
||||||
if cursorPosX > width then
|
|
||||||
scrollPosX = cursorPosX - width + 1
|
|
||||||
cursorPosX = width
|
|
||||||
end
|
|
||||||
tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1] .. tmpdata[cursorPosY + 1]
|
|
||||||
table.remove(tmpdata, cursorPosY + 1)
|
|
||||||
renderFlag = true
|
|
||||||
else
|
|
||||||
tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1]:sub(1, cursorPosX + scrollPosX - 3) .. tmpdata[cursorPosY + scrollPosY - 1]:sub(cursorPosX + scrollPosX - 1)
|
|
||||||
cursorPosX = math.min(cursorPosX - 1, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) + 1)
|
|
||||||
if cursorPosX < 1 then
|
|
||||||
cursorPosX = 1
|
|
||||||
scrollPosX = scrollPosX - 1
|
|
||||||
renderFlag = true
|
|
||||||
else
|
|
||||||
gpu.set(1, cursorPosY, tmpdata[cursorPosY + scrollPosY - 1]:sub(scrollPosX) .. " ")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if key == "tab" then
|
|
||||||
changesMade = true
|
|
||||||
cursorRenderFlag = true
|
|
||||||
cursorWhite = true
|
|
||||||
tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1]:sub(1, cursorPosX + scrollPosX - 2) .. tab .. tmpdata[cursorPosY + scrollPosY - 1]:sub(cursorPosX + scrollPosX - 1)
|
|
||||||
cursorPosX = cursorPosX + unicode.wlen(tab)
|
|
||||||
if cursorPosX > width then
|
|
||||||
scrollPosX = scrollPosX + cursorPosX - width
|
|
||||||
cursorPosX = width
|
|
||||||
renderFlag = true
|
|
||||||
else
|
|
||||||
gpu.set(1, cursorPosY, tmpdata[cursorPosY + scrollPosY - 1]:sub(scrollPosX))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if args[3] >= 32 and args[3] <= 126 then
|
|
||||||
changesMade = true
|
|
||||||
cursorRenderFlag = true
|
|
||||||
cursorWhite = true
|
|
||||||
tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1]:sub(1, cursorPosX + scrollPosX - 2) .. unicode.char(args[3]) .. tmpdata[cursorPosY + scrollPosY - 1]:sub(cursorPosX + scrollPosX - 1)
|
|
||||||
cursorPosX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1])) + 1
|
|
||||||
--ocelot.log(tostring(cursorPosX))
|
|
||||||
if cursorPosX > width then
|
|
||||||
cursorPosX = width
|
|
||||||
scrollPosX = scrollPosX + 1
|
|
||||||
renderFlag = true
|
|
||||||
else
|
|
||||||
gpu.set(1, cursorPosY, tmpdata[cursorPosY + scrollPosY - 1]:sub(scrollPosX))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif args[1] == "scroll" then
|
|
||||||
if args[5] == 1 then
|
|
||||||
for i = 1, scrollSpeed do
|
|
||||||
scrollUp()
|
|
||||||
end
|
|
||||||
elseif args[5] == -1 and cursorPosY < #tmpdata then
|
|
||||||
for i = 1, scrollSpeed do
|
|
||||||
scrollDown()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return renderFlag, cursorRenderFlag
|
|
||||||
end
|
|
||||||
|
|
||||||
local function save()
|
|
||||||
gpu.setBackground(0xFFFFFF)
|
|
||||||
gpu.setForeground(0)
|
|
||||||
gpu.set(1, height - 1, string.rep(" ", width))
|
|
||||||
termlib.cursorPosX = 1
|
|
||||||
termlib.cursorPosY = height - 1
|
|
||||||
local savepath = read(nil, "\27[107m\27[30mSave location: ", filepath)
|
|
||||||
gpu.setBackground(0xFFFFFF)
|
|
||||||
gpu.setForeground(0)
|
|
||||||
if fs.exists(savepath) then
|
|
||||||
gpu.set(1, height - 1, string.rep(" ", width))
|
|
||||||
local answer = read(nil, "\27[107m\27[30mFile already exists. Overwrite it? [Y/n] ")
|
|
||||||
if answer:lower() == "n" then
|
|
||||||
gpu.setBackground(0xFFFFFF)
|
|
||||||
gpu.setForeground(0)
|
|
||||||
gpu.set(1, height - 1, filestring .. string.rep(" ", width))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local handle, errorMessage = fs.open(savepath, "w")
|
|
||||||
if handle then
|
|
||||||
if table.concat(tmpdata, "\n"):sub(-1, -1) == "\n" then
|
|
||||||
handle:write(table.concat(tmpdata, "\n"))
|
|
||||||
else
|
|
||||||
handle:write(table.concat(tmpdata, "\n") .. "\n") -- add a newline at the end to follow POSIX standards
|
|
||||||
end
|
|
||||||
handle:close()
|
|
||||||
gpu.set(1, height - 1, filestring .. string.rep(" ", width))
|
|
||||||
else
|
|
||||||
gpu.set(1, height - 1, "ERROR: " .. errorMessage:gsub("\n", "") .. string.rep(" ", width))
|
|
||||||
end
|
|
||||||
changesMade = false
|
|
||||||
end
|
|
||||||
|
|
||||||
render()
|
|
||||||
while true do
|
|
||||||
local args = {event.pull(0.5)}
|
|
||||||
local renderFlag, cursorRenderFlag, specialKey = false, false, nil
|
|
||||||
local previousCursorX, previousCursorY = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2), cursorPosY
|
|
||||||
if args and args[1] then
|
|
||||||
cursorWhite = true
|
|
||||||
renderFlag, cursorRenderFlag, specialKey = processEvent(args)
|
|
||||||
if specialKey == "x" then
|
|
||||||
if changesMade then
|
|
||||||
termlib.cursorPosX = 1
|
|
||||||
termlib.cursorPosY = height - 1
|
|
||||||
local response = read(nil, "\27[107m\27[30mWould you like to save changes? [Y/n] ")
|
|
||||||
if response:lower() ~= "n" then
|
|
||||||
save()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
gpu.freeAllBuffers()
|
|
||||||
clear()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if specialKey == "s" then
|
|
||||||
save()
|
|
||||||
end
|
|
||||||
repeat
|
|
||||||
args = {event.pull("key_down", 0)}
|
|
||||||
if args and args[1] then
|
|
||||||
processEvent(args)
|
|
||||||
end
|
|
||||||
until not args or not args[1]
|
|
||||||
else
|
|
||||||
cursorWhite = not cursorWhite
|
|
||||||
cursorRenderFlag = true
|
|
||||||
end
|
|
||||||
if cursorRenderFlag then
|
|
||||||
local char = gpu.get(previousCursorX, previousCursorY)
|
|
||||||
gpu.set(previousCursorX, previousCursorY, char)
|
|
||||||
local realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2)
|
|
||||||
if realCursorX < 1 then
|
|
||||||
scrollPosX = scrollPosX + realCursorX - 1
|
|
||||||
cursorPosX = 1
|
|
||||||
realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2)
|
|
||||||
end
|
|
||||||
local char = gpu.get(realCursorX, cursorPosY)
|
|
||||||
if cursorWhite then
|
|
||||||
gpu.setBackground(0xFFFFFF)
|
|
||||||
gpu.setForeground(0)
|
|
||||||
end
|
|
||||||
gpu.set(realCursorX, cursorPosY, char)
|
|
||||||
if cursorWhite then
|
|
||||||
gpu.setForeground(0xFFFFFF)
|
|
||||||
gpu.setBackground(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if renderFlag then
|
|
||||||
render()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-70
@@ -1,70 +1 @@
|
|||||||
local component = import("component")
|
Error: no such file. (``)
|
||||||
local computer = import("computer")
|
|
||||||
|
|
||||||
local function printstat(text)
|
|
||||||
termlib.cursorPosX = 35
|
|
||||||
termlib.write(text .. "\n", false)
|
|
||||||
end
|
|
||||||
|
|
||||||
termlib.write(_OSLOGO, false)
|
|
||||||
termlib.cursorPosY = termlib.cursorPosY - 17
|
|
||||||
printstat("\27[92mOS\27[0m: ".._OSVERSION)
|
|
||||||
printstat("\27[92mArchitecture\27[0m: ".._VERSION)
|
|
||||||
local componentCounter = 0
|
|
||||||
for _, _ in component.list() do
|
|
||||||
componentCounter = componentCounter + 1
|
|
||||||
end
|
|
||||||
printstat("\27[92mComponents\27[0m: "..tostring(componentCounter))
|
|
||||||
printstat("\27[92mCoroutines\27[0m: "..tostring(#cormgr.corList))
|
|
||||||
printstat("\27[92mBattery\27[0m: "..tostring(math.floor(computer.energy() / computer.maxEnergy() * 1000 + 0.5) / 10).."%")
|
|
||||||
local totalMemory = computer.totalMemory()
|
|
||||||
local usedMemory = computer.totalMemory() - computer.freeMemory()
|
|
||||||
local totalMemoryString
|
|
||||||
if convert(totalMemory, "B", "GiB") >= 1 then
|
|
||||||
totalMemoryString = tostring(math.floor(convert(totalMemory, "B", "GiB") * 100 + 0.5) / 100) .. " GiB"
|
|
||||||
elseif convert(totalMemory, "B", "MiB") >= 1 then
|
|
||||||
totalMemoryString = tostring(math.floor(convert(totalMemory, "B", "MiB") * 100 + 0.5) / 100) .. " MiB"
|
|
||||||
elseif convert(totalMemory, "B", "KiB") >= 1 then
|
|
||||||
totalMemoryString = tostring(math.floor(convert(totalMemory, "B", "KiB") * 100 + 0.5) / 100) .. " KiB"
|
|
||||||
else
|
|
||||||
totalMemoryString = tostring(totalMemory) .. " B"
|
|
||||||
end
|
|
||||||
local usedMemoryString
|
|
||||||
if convert(usedMemory, "B", "GiB") >= 1 then
|
|
||||||
usedMemoryString = tostring(math.floor(convert(usedMemory, "B", "GiB") * 100 + 0.5) / 100) .. " GiB"
|
|
||||||
elseif convert(usedMemory, "B", "MiB") >= 1 then
|
|
||||||
usedMemoryString = tostring(math.floor(convert(usedMemory, "B", "MiB") * 100 + 0.5) / 100) .. " MiB"
|
|
||||||
elseif convert(usedMemory, "B", "KiB") >= 1 then
|
|
||||||
usedMemoryString = tostring(math.floor(convert(usedMemory, "B", "KiB") * 100 + 0.5) / 100) .. " KiB"
|
|
||||||
else
|
|
||||||
usedMemoryString = tostring(usedMemory) .. " B"
|
|
||||||
end
|
|
||||||
printstat("\27[92mMemory\27[0m: "..usedMemoryString.." / "..totalMemoryString)
|
|
||||||
local totalDisk = component.invoke(computer.getBootAddress(), "spaceTotal")
|
|
||||||
local usedDisk = component.invoke(computer.getBootAddress(), "spaceUsed")
|
|
||||||
local totalDiskString
|
|
||||||
if convert(totalDisk, "B", "GiB") >= 1 then
|
|
||||||
totalDiskString = tostring(math.floor(convert(totalDisk, "B", "GiB") * 100 + 0.5) / 100) .. " GiB"
|
|
||||||
elseif convert(totalDisk, "B", "MiB") >= 1 then
|
|
||||||
totalDiskString = tostring(math.floor(convert(totalDisk, "B", "MiB") * 100 + 0.5) / 100) .. " MiB"
|
|
||||||
elseif convert(totalDisk, "B", "KiB") >= 1 then
|
|
||||||
totalDiskString = tostring(math.floor(convert(totalDisk, "B", "KiB") * 100 + 0.5) / 100) .. " KiB"
|
|
||||||
else
|
|
||||||
totalDiskString = tostring(totalDisk) .. " B"
|
|
||||||
end
|
|
||||||
local usedDiskString
|
|
||||||
if convert(usedDisk, "B", "GiB") >= 1 then
|
|
||||||
usedDiskString = tostring(math.floor(convert(usedDisk, "B", "GiB") * 100 + 0.5) / 100) .. " GiB"
|
|
||||||
elseif convert(usedDisk, "B", "MiB") >= 1 then
|
|
||||||
usedDiskString = tostring(math.floor(convert(usedDisk, "B", "MiB") * 100 + 0.5) / 100) .. " MiB"
|
|
||||||
elseif convert(usedDisk, "B", "KiB") >= 1 then
|
|
||||||
usedDiskString = tostring(math.floor(convert(usedDisk, "B", "KiB") * 100 + 0.5) / 100) .. " KiB"
|
|
||||||
else
|
|
||||||
usedDiskString = tostring(usedDisk) .. " B"
|
|
||||||
end
|
|
||||||
printstat("\27[92mDisk\27[0m: "..usedDiskString.." / "..totalDiskString)
|
|
||||||
local width, height = component.invoke(component.list("gpu")(), "getResolution")
|
|
||||||
printstat("\27[92mResolution\27[0m: "..tostring(width).."x"..tostring(height).."\n")
|
|
||||||
printstat("\27[40m \27[41m \27[42m \27[43m \27[44m \27[45m \27[46m \27[47m ")
|
|
||||||
printstat("\27[100m \27[101m \27[102m \27[103m \27[104m \27[105m \27[106m \27[107m ")
|
|
||||||
termlib.cursorPosY = termlib.cursorPosY + 5
|
|
||||||
|
|||||||
+1
-38
@@ -1,38 +1 @@
|
|||||||
local fs = import("filesystem")
|
Error: no such file. (``)
|
||||||
local args = {...}
|
|
||||||
local command = args[1]
|
|
||||||
args = nil
|
|
||||||
if not command then
|
|
||||||
local handle, data, tmpdata = fs.open("/halyde/apps/helpdb/default.txt", "r"), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
print(data)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if shell.aliases[command] then
|
|
||||||
command = shell.aliases[command]
|
|
||||||
end
|
|
||||||
if fs.exists("/halyde/apps/helpdb/" .. command .. ".txt") then
|
|
||||||
local handle, data, tmpdata = fs.open("/halyde/apps/helpdb/" .. command .. ".txt", "r"), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
print(data)
|
|
||||||
local aliases = table.copy(shell.aliases)
|
|
||||||
if table.find(aliases, command) then
|
|
||||||
local aliasIndex = table.find(aliases, command)
|
|
||||||
local aliasString = "Aliases:\n " .. aliasIndex
|
|
||||||
aliases[aliasIndex] = nil
|
|
||||||
while table.find(aliases, command) do
|
|
||||||
aliasIndex = table.find(aliases, command)
|
|
||||||
aliasString = aliasString .. ", " .. aliasIndex
|
|
||||||
aliases[aliasIndex] = nil
|
|
||||||
end
|
|
||||||
print(aliasString)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
print("Could not find help file for: " .. command .. ".")
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-92
@@ -1,92 +1 @@
|
|||||||
local component = import("component")
|
Error: no such file. (``)
|
||||||
local computer = import("computer")
|
|
||||||
local args = {...}
|
|
||||||
if not args then return print("\x1b[91mCannot get arguments.") end
|
|
||||||
if not args[1] then
|
|
||||||
return shell.run("help label")
|
|
||||||
end
|
|
||||||
local inputID = args[1]
|
|
||||||
local comp
|
|
||||||
|
|
||||||
local function componentFromSlot(slotNum)
|
|
||||||
if slotNum>=5 and slotNum<=8 then slotNum=slotNum-1 end
|
|
||||||
for i,v in component.list() do
|
|
||||||
if component.slot(i)==slotNum then
|
|
||||||
comp=component.proxy(i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if inputID=="eeprom" then
|
|
||||||
comp = component.eeprom
|
|
||||||
elseif inputID=="halyde" then
|
|
||||||
comp = component.proxy(computer.getBootAddress())
|
|
||||||
elseif inputID:sub(1,4)=="slot" and tonumber(inputID:sub(5)) then
|
|
||||||
local slotNum = tonumber(inputID:sub(5))-1
|
|
||||||
componentFromSlot(slotNum)
|
|
||||||
elseif inputID:sub(1,1)=="#" and tonumber(inputID:sub(2)) then
|
|
||||||
local slotNum = tonumber(inputID:sub(2))+5
|
|
||||||
componentFromSlot(slotNum)
|
|
||||||
elseif #inputID>=3 then
|
|
||||||
local fullID = component.get(inputID)
|
|
||||||
if not fullID then return print("\x1b[91mCould not find entire component ID from \""..inputID.."\".") end
|
|
||||||
comp = component.proxy(fullID)
|
|
||||||
else
|
|
||||||
print("\x1b[91mAddress must have atleast 3 characters")
|
|
||||||
return shell.run("help label")
|
|
||||||
end
|
|
||||||
if not comp then
|
|
||||||
return print("\x1b[91mCould not find component from \""..inputID.."\".")
|
|
||||||
end
|
|
||||||
local compID = comp.address
|
|
||||||
|
|
||||||
local function formatID(id)
|
|
||||||
return id:sub(1,8).."\x1b[37m"..id:sub(9).."\x1b[39m"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function unsupported(act)
|
|
||||||
print("This \x1b[92m"..(comp.type or "unknown").."\x1b[39m component doesn't support "..act.." labels.\nID: "..formatID(compID))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function compError(act,reason)
|
|
||||||
print("\x1b[91mAn error occured while "..act.." the label of this component.\nComponent: "..(compID or "unknown id").." ("..((comp or {}).type or "unknown type")..")\n\n"..reason)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function formatLabel(label)
|
|
||||||
local res = "No label defined"
|
|
||||||
if label then res="\""..label.."\"" end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(args[2])~="string" then
|
|
||||||
if not comp.getLabel then
|
|
||||||
return unsupported("getting")
|
|
||||||
end
|
|
||||||
local label
|
|
||||||
local success,reason = pcall(function()
|
|
||||||
label = comp.getLabel()
|
|
||||||
end)
|
|
||||||
if success then
|
|
||||||
print("Label of "..formatID(compID)..((comp.type and comp.type~="filesystem") and " ("..comp.type..")" or "")..":\n \x1b[92m"..formatLabel(label).."\x1b[39m")
|
|
||||||
else
|
|
||||||
compError("getting",reason)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if not comp.setLabel then
|
|
||||||
return unsupported("setting")
|
|
||||||
end
|
|
||||||
local newLabel = ""
|
|
||||||
for i=2,#args do
|
|
||||||
newLabel=newLabel..tostring(args[i])
|
|
||||||
if i<#args then newLabel=newLabel.." " end
|
|
||||||
end
|
|
||||||
local label
|
|
||||||
local success,reason = pcall(function()
|
|
||||||
label = comp.setLabel(newLabel)
|
|
||||||
end)
|
|
||||||
if success then
|
|
||||||
print("Successfully set label of "..formatID(compID)..(comp.type and " ("..comp.type..")" or "").." to:\n \x1b[92m"..formatLabel(label).."\x1b[39m")
|
|
||||||
else
|
|
||||||
compError("setting",reason)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-70
@@ -1,70 +1 @@
|
|||||||
local args = {...}
|
Error: no such file. (``)
|
||||||
local target = args[1]
|
|
||||||
args = nil
|
|
||||||
local fs = import("filesystem")
|
|
||||||
local unicode = import("unicode")
|
|
||||||
local maxLength = 0
|
|
||||||
local margin = 2 -- minimum space between filename and size
|
|
||||||
local dirTable = {}
|
|
||||||
local fileTable = {}
|
|
||||||
|
|
||||||
if target then
|
|
||||||
if target:sub(1, 1) ~= "/" then
|
|
||||||
target = fs.concat(shell.workingDirectory, target)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
target = shell.workingDirectory
|
|
||||||
end
|
|
||||||
|
|
||||||
local files = fs.list(target)
|
|
||||||
|
|
||||||
if files then
|
|
||||||
for _, file in pairs(files) do
|
|
||||||
if file:sub(-1, -1) == "/" then
|
|
||||||
table.insert(dirTable, file)
|
|
||||||
file = file:sub(1, -2)
|
|
||||||
else
|
|
||||||
table.insert(fileTable, file)
|
|
||||||
end
|
|
||||||
if unicode.wlen(file) > maxLength then
|
|
||||||
maxLength = unicode.wlen(file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(dirTable)
|
|
||||||
table.sort(fileTable)
|
|
||||||
files = {}
|
|
||||||
for _, v in ipairs(dirTable) do
|
|
||||||
table.insert(files, v)
|
|
||||||
end
|
|
||||||
for _, v in ipairs(fileTable) do
|
|
||||||
table.insert(files, v)
|
|
||||||
end
|
|
||||||
dirTable, fileTable = nil, nil
|
|
||||||
for _, file in ipairs(files) do
|
|
||||||
local dir = false
|
|
||||||
local filetext
|
|
||||||
if file:sub(-1, -1) == "/" then
|
|
||||||
dir = true
|
|
||||||
filetext = "\27[93m"..file:sub(1, -2)
|
|
||||||
elseif file:find(".") and file:match("[^.]+$") == "lua" then
|
|
||||||
filetext = "\27[92m"..file
|
|
||||||
end
|
|
||||||
filetext = (filetext or file)..string.rep(" ", maxLength - unicode.wlen(file) + margin)
|
|
||||||
if dir then
|
|
||||||
print(filetext.." \27[0m[DIR]")
|
|
||||||
else
|
|
||||||
local size = fs.size(fs.concat(target, file))
|
|
||||||
local sizeString
|
|
||||||
if convert(size, "B", "GiB") >= 1 then
|
|
||||||
sizeString = tostring(math.floor(convert(size, "B", "GiB") * 100 + 0.5) / 100).." GiB"
|
|
||||||
elseif convert(size, "B", "MiB") >= 1 then
|
|
||||||
sizeString = tostring(math.floor(convert(size, "B", "MiB") * 100 + 0.5) / 100).." MiB"
|
|
||||||
elseif convert(size, "B", "KiB") >= 1 then
|
|
||||||
sizeString = tostring(math.floor(convert(size, "B", "KiB") * 100 + 0.5) / 100).." KiB"
|
|
||||||
else
|
|
||||||
sizeString = tostring(size).." B"
|
|
||||||
end
|
|
||||||
print(filetext.."\27[0m"..sizeString)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -1,9 +1 @@
|
|||||||
print("\27[93m"..tostring(#cormgr.corList).."\27[0m coroutines active")
|
Error: no such file. (``)
|
||||||
for i=1, #cormgr.corList do
|
|
||||||
if i==#cormgr.corList then
|
|
||||||
print("\27[93m└ "..i.."\27[0m - "..cormgr.labelList[i].." \27[0m")
|
|
||||||
else
|
|
||||||
print("\27[93m├ "..i.."\27[0m - "..cormgr.labelList[i].." \27[0m")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-320
@@ -1,320 +1 @@
|
|||||||
local serialize = import("serialize")
|
Error: no such file. (``)
|
||||||
local component = import("component")
|
|
||||||
local computer = import("computer")
|
|
||||||
local unicode = import("unicode")
|
|
||||||
|
|
||||||
local width,height = component.gpu.getResolution()
|
|
||||||
|
|
||||||
local args = {...}
|
|
||||||
local showAll = not not (table.find(args,"-a") or table.find(args,"--all"))
|
|
||||||
|
|
||||||
local tablePos = {}
|
|
||||||
local tableOut = {}
|
|
||||||
local headers = {
|
|
||||||
["slot"]="SLOT",
|
|
||||||
["capacity"]="CAPACITY",
|
|
||||||
["managed"]="MANAGED",
|
|
||||||
["readOnly"]="READ-ONLY",
|
|
||||||
["id"]="FULL COMPONENT ID",
|
|
||||||
["mount"]="MOUNT",
|
|
||||||
["bootable"]="BOOTABLE",
|
|
||||||
["label"]="LABEL"
|
|
||||||
}
|
|
||||||
local headerAlign = {
|
|
||||||
["slot"]=false,
|
|
||||||
["capacity"]=true,
|
|
||||||
["managed"]=false,
|
|
||||||
["readOnly"]=false,
|
|
||||||
["id"]=false,
|
|
||||||
["mount"]=false,
|
|
||||||
["bootable"]=false,
|
|
||||||
["label"]=false
|
|
||||||
}
|
|
||||||
local function addHeader(id)
|
|
||||||
table.insert(tableOut,{headerAlign[id],headers[id]})
|
|
||||||
tablePos[id]=#tableOut
|
|
||||||
end
|
|
||||||
for i,v in pairs(tablePos) do print(i,v) end
|
|
||||||
|
|
||||||
local function everyHeader()
|
|
||||||
addHeader("slot")
|
|
||||||
addHeader("capacity")
|
|
||||||
addHeader("managed")
|
|
||||||
addHeader("readOnly")
|
|
||||||
addHeader("id")
|
|
||||||
addHeader("mount")
|
|
||||||
addHeader("bootable")
|
|
||||||
addHeader("label")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function defaultHeaders()
|
|
||||||
if width>100 then addHeader("slot") end
|
|
||||||
addHeader("capacity")
|
|
||||||
if width>80 then addHeader("id") end
|
|
||||||
addHeader("mount")
|
|
||||||
addHeader("label")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function invalidArgSyntax(err)
|
|
||||||
print(err)
|
|
||||||
return shell.run("help lsdrv")
|
|
||||||
end
|
|
||||||
|
|
||||||
local outArgIdx = table.find(args,"-o") or table.find(args,"--output")
|
|
||||||
if showAll then
|
|
||||||
everyHeader()
|
|
||||||
elseif outArgIdx then
|
|
||||||
table.remove(args,outArgIdx)
|
|
||||||
local arg = table.remove(args,outArgIdx)
|
|
||||||
if not arg then return invalidArgSyntax("Argument -o must have a value") end
|
|
||||||
if arg=="all" then
|
|
||||||
everyHeader()
|
|
||||||
else
|
|
||||||
if arg:sub(1,1)=="+" then
|
|
||||||
defaultHeaders()
|
|
||||||
end
|
|
||||||
for word in string.gmatch(arg:sub(2),"([^,]+)") do
|
|
||||||
if headers[word] then
|
|
||||||
addHeader(word)
|
|
||||||
else
|
|
||||||
print("\x1b[93mCategory \""..word.."\" doesn't exist\x1b[39m")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
defaultHeaders()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function headerUsed(id)
|
|
||||||
return not not tablePos[id]
|
|
||||||
end
|
|
||||||
|
|
||||||
local bibytes = not (table.find(args,"-H") or table.find(args,"--si"))
|
|
||||||
local function formatSize(mem)
|
|
||||||
local units = bibytes and {"B","KiB","MiB","GiB"} or {"B","KB","MB","GB"}
|
|
||||||
local unit = 1
|
|
||||||
local unitStep = bibytes and 1024 or 1000
|
|
||||||
while mem > unitStep and units[unit] do
|
|
||||||
unit = unit + 1
|
|
||||||
mem = mem/unitStep
|
|
||||||
end
|
|
||||||
local memStr = tostring(mem):sub(1,5)
|
|
||||||
if unicode.sub(memStr,-2)==".0" then
|
|
||||||
memStr=unicode.sub(memStr,1,-3)
|
|
||||||
end
|
|
||||||
return memStr.." "..units[unit]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function fileExists(proxy,path)
|
|
||||||
return proxy.exists(path) and not proxy.isDirectory(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getBootCode(proxy)
|
|
||||||
local sector1 = proxy.readSector(1)
|
|
||||||
for i = 1,#sector1 do
|
|
||||||
if sector1:sub(i,i)=="\0" then
|
|
||||||
sector1 = sector1:sub(1,i-1)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return sector1
|
|
||||||
end
|
|
||||||
|
|
||||||
local function isBootable(proxy,type)
|
|
||||||
if type=="drive" then
|
|
||||||
return not not load(getBootCode(proxy))
|
|
||||||
elseif type=="filesystem" then
|
|
||||||
return not not (fileExists(proxy,"/init.lua") or fileExists(proxy,"/OS.lua"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function formatBoolean(bool)
|
|
||||||
return ({[true]="Yes",[false]="No"})[bool]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function handleComponent(id,type)
|
|
||||||
if not id then return end
|
|
||||||
local proxy = component.proxy(id)
|
|
||||||
if not proxy then return end
|
|
||||||
-- local out = {}
|
|
||||||
-- for i=1,#tableOut do table.insert(out,"unknown") end
|
|
||||||
local slot,capacity,managed,readOnly,mount,bootable,label
|
|
||||||
|
|
||||||
local cslot = component.slot(id)
|
|
||||||
if cslot==-1 then
|
|
||||||
slot="Virtual"
|
|
||||||
elseif cslot==9 then
|
|
||||||
slot="EEPROM"
|
|
||||||
else
|
|
||||||
slot="#"..(cslot+2)
|
|
||||||
end
|
|
||||||
|
|
||||||
managed="Yes"
|
|
||||||
readOnly="No"
|
|
||||||
if type=="drive" then
|
|
||||||
managed="No"
|
|
||||||
capacity=formatSize(proxy.getCapacity())
|
|
||||||
mount="/special/drive/"..id:sub(1,3)
|
|
||||||
elseif type=="filesystem" then
|
|
||||||
capacity=formatSize(proxy.spaceTotal())
|
|
||||||
if proxy.isReadOnly() then
|
|
||||||
readOnly="Yes"
|
|
||||||
end
|
|
||||||
mount="/mnt/"..id:sub(1,3)
|
|
||||||
if computer.getBootAddress()==id then mount="/" end
|
|
||||||
if computer.tmpAddress()==id then
|
|
||||||
mount="/tmp"
|
|
||||||
slot="Temporary"
|
|
||||||
end
|
|
||||||
elseif type=="eeprom" then
|
|
||||||
capacity=formatSize(proxy.getSize()+proxy.getDataSize())
|
|
||||||
mount="/special/eeprom/"
|
|
||||||
end
|
|
||||||
|
|
||||||
if headerUsed("bootable") then
|
|
||||||
bootable = formatBoolean(isBootable(proxy,type))
|
|
||||||
end
|
|
||||||
|
|
||||||
if proxy.getLabel then
|
|
||||||
local clabel = proxy.getLabel()
|
|
||||||
label=clabel and serialize.string(clabel) or "None"
|
|
||||||
else
|
|
||||||
label="Unsupported"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function insertElement(i,v)
|
|
||||||
if not tablePos[i] then return end
|
|
||||||
table.insert(tableOut[tablePos[i]],v or "unknown")
|
|
||||||
end
|
|
||||||
insertElement("slot",slot)
|
|
||||||
insertElement("capacity",capacity)
|
|
||||||
insertElement("managed",managed)
|
|
||||||
insertElement("readOnly",readOnly)
|
|
||||||
insertElement("id",id)
|
|
||||||
insertElement("mount",mount)
|
|
||||||
insertElement("bootable",bootable)
|
|
||||||
insertElement("label",label)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function filter(tbl,test)
|
|
||||||
local i=1
|
|
||||||
while i<=#tbl do
|
|
||||||
if not test(tbl[i]) then
|
|
||||||
table.remove(tbl,i)
|
|
||||||
i=i-1
|
|
||||||
end
|
|
||||||
i=i+1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function luaExpr(arg)
|
|
||||||
local func,err = load("local component,computer,type,id,readonly,capacity=... local managed,eeprom,halyde,tmp,proxy,slot,all=type==\"drive\",type==\"eeprom\",id==\""..computer.getBootAddress().."\",id==\""..computer.tmpAddress().."\",component.proxy(id),component.slot(id)+2,true return "..arg)
|
|
||||||
if func then
|
|
||||||
return function(comp)
|
|
||||||
local readOnly,capacity=false,nil
|
|
||||||
if comp[2]=="filesystem" then
|
|
||||||
readOnly=component.invoke(comp[1],"isReadOnly")
|
|
||||||
capacity=component.invoke(comp[1],"spaceTotal")
|
|
||||||
elseif comp[2]=="drive" then
|
|
||||||
capacity=component.invoke(comp[1],"getCapacity")
|
|
||||||
elseif comp[2]=="eeprom" then
|
|
||||||
capacity=component.invoke(comp[1],"getSize")+component.invoke(comp[1],"getDataSize")
|
|
||||||
end
|
|
||||||
return func(component,computer,comp[2],comp[1],readOnly,capacity)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return nil,err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function boolToNum(val)
|
|
||||||
if type(val)=="boolean" then
|
|
||||||
if val then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
|
|
||||||
local comps = {}
|
|
||||||
for i,v in component.list("filesystem") do table.insert(comps,{i,v}) end
|
|
||||||
for i,v in component.list("drive") do table.insert(comps,{i,v}) end
|
|
||||||
for i,v in component.list("eeprom") do table.insert(comps,{i,v}) end
|
|
||||||
|
|
||||||
if not showAll then
|
|
||||||
local showArgIdx = table.find(args,"-s") or table.find(args,"--show")
|
|
||||||
if showArgIdx then
|
|
||||||
table.remove(args,showArgIdx)
|
|
||||||
local arg = table.remove(args,showArgIdx)
|
|
||||||
if not arg then return invalidArgSyntax("Argument -s must have a value") end
|
|
||||||
local func,err = luaExpr(arg)
|
|
||||||
if func then
|
|
||||||
filter(comps,func)
|
|
||||||
else
|
|
||||||
return print("\x1b[91mInvalid component filter:\n\n"..tostring(err).."\x1b[39m")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
filter(comps,function(comp)
|
|
||||||
return comp[2]~="eeprom" and comp[1]~=computer.tmpAddress()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local sortArgIdx = table.find(args,"-S") or table.find(args,"--sort")
|
|
||||||
if sortArgIdx then
|
|
||||||
table.remove(args,sortArgIdx)
|
|
||||||
local arg = table.remove(args,sortArgIdx)
|
|
||||||
if not arg then return invalidArgSyntax("Argument -S must have a value") end
|
|
||||||
local func,err = luaExpr(arg)
|
|
||||||
if func then
|
|
||||||
table.sort(comps,function(a,b)
|
|
||||||
return (boolToNum(func(a)) or 0)<(boolToNum(func(b)) or 0)
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
return print("\x1b[91mInvalid sort expression:\n\n"..tostring(err).."\x1b[39m")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.sort(comps,function(a,b)
|
|
||||||
return a[1]<b[1]
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
if #comps>0 then
|
|
||||||
for i,v in ipairs(comps) do handleComponent(v[1],v[2]) end
|
|
||||||
else
|
|
||||||
return print("Could not find storage components for this filter.")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function renderTableOutput()
|
|
||||||
local lines = {}
|
|
||||||
for i=1,#tableOut[1]-1 do
|
|
||||||
table.insert(lines,"")
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=1,#tableOut do
|
|
||||||
local length = 1
|
|
||||||
for j=1,#lines do
|
|
||||||
tableOut[i][j+1]=tableOut[i][j+1] or "[nil]"
|
|
||||||
length = math.max(length,unicode.wlen(tableOut[i][j+1]))
|
|
||||||
end
|
|
||||||
for j=1,#lines do
|
|
||||||
if tableOut[i][1] then
|
|
||||||
lines[j]=lines[j]..string.rep(" ",length-unicode.wlen(tableOut[i][j+1]))..tableOut[i][j+1]
|
|
||||||
elseif i<#tableOut then
|
|
||||||
lines[j]=lines[j]..tableOut[i][j+1]..string.rep(" ",length-unicode.wlen(tableOut[i][j+1]))
|
|
||||||
else
|
|
||||||
lines[j]=lines[j]..tableOut[i][j+1]
|
|
||||||
end
|
|
||||||
if i<#tableOut then lines[j]=lines[j].." " end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
print(lines[1].."\n")
|
|
||||||
for i=2,#lines do
|
|
||||||
print(lines[i])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
renderTableOutput()
|
|
||||||
|
|||||||
+1
-31
@@ -1,31 +1 @@
|
|||||||
print("\27[44m".._VERSION.."\27[0m shell")
|
Error: no such file. (``)
|
||||||
print('Type "exit" to exit.')
|
|
||||||
termlib.readHistory["lua"] = {""}
|
|
||||||
local fs = import("filesystem")
|
|
||||||
|
|
||||||
local loadedLibraries = ""
|
|
||||||
local libList = fs.list("halyde/lib")
|
|
||||||
for _, lib in pairs(libList) do
|
|
||||||
if lib:match("(.+)%.lua") then
|
|
||||||
loadedLibraries = loadedLibraries .. "local " .. lib:match("(.+)%.lua") .. ' = import("' .. lib:match("(.+)%.lua") .. '")\n'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local command = read("lua", "\27[44mlua>\27[0m ")
|
|
||||||
if command == "exit" then
|
|
||||||
return
|
|
||||||
else
|
|
||||||
local function runCommand()
|
|
||||||
local func = load(loadedLibraries.."return "..command,"=stdin") or load(loadedLibraries..command,"=stdin")
|
|
||||||
local res = {assert(func)()}
|
|
||||||
if res and (type(res[1])~="nil" or type(res[2])~="nil") then print(table.unpack(res)) end
|
|
||||||
end
|
|
||||||
local result, reason = xpcall(runCommand, function(errMsg)
|
|
||||||
return errMsg .. "\n\n" .. debug.traceback()
|
|
||||||
end)
|
|
||||||
if not result then
|
|
||||||
print("\27[91m" .. reason)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -1,8 +1 @@
|
|||||||
local computer = import("computer")
|
Error: no such file. (``)
|
||||||
|
|
||||||
if type(computer)~="table" then
|
|
||||||
return print("\x1b[91mComputer library returned '"..type(computer).."' type\x1b[39m")
|
|
||||||
end
|
|
||||||
|
|
||||||
local address = computer.getBootAddress()
|
|
||||||
print(address)
|
|
||||||
|
|||||||
+1
-14
@@ -1,14 +1 @@
|
|||||||
local directory = ...
|
Error: no such file. (``)
|
||||||
local fs = import("filesystem")
|
|
||||||
|
|
||||||
if not directory then
|
|
||||||
shell.run("help mkdir")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if directory:sub(1, 1) ~= "/" then
|
|
||||||
directory = fs.concat(shell.workingDirectory, directory)
|
|
||||||
end
|
|
||||||
if fs.exists(directory) then
|
|
||||||
print("\27[91mAn object already exists at the specified path.")
|
|
||||||
end
|
|
||||||
fs.makeDirectory(directory)
|
|
||||||
|
|||||||
+1
-24
@@ -1,24 +1 @@
|
|||||||
local fromFile, toFile = ...
|
Error: no such file. (``)
|
||||||
local fs = import("filesystem")
|
|
||||||
|
|
||||||
if not fromFile or not toFile then
|
|
||||||
shell.run("help mv")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if fromFile:sub(1, 1) ~= "/" then
|
|
||||||
fromFile = fs.concat(shell.workingDirectory, fromFile)
|
|
||||||
end
|
|
||||||
if toFile:sub(1, 1) ~= "/" then
|
|
||||||
toFile = fs.concat(shell.workingDirectory, toFile)
|
|
||||||
end
|
|
||||||
if fromFile == toFile then
|
|
||||||
print("\27[91mSource and destination are the same.")
|
|
||||||
end
|
|
||||||
if not fs.exists(fromFile) then
|
|
||||||
print("\27[91mSource file does not exist.")
|
|
||||||
end
|
|
||||||
if fs.exists(toFile) and not (table.find({...}, "-o") or table.find({...}, "--overwrite")) then
|
|
||||||
print("\27[91mDestination file already exists. Run this command again with -o to overwrite it.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
fs.rename(fromFile, toFile)
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
import("computer").shutdown(true)
|
Error: no such file. (``)
|
||||||
|
|||||||
+1
-15
@@ -1,15 +1 @@
|
|||||||
local file = ...
|
Error: no such file. (``)
|
||||||
local fs = import("filesystem")
|
|
||||||
|
|
||||||
if not file then
|
|
||||||
shell.run("help rm")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if file:sub(1, 1) ~= "/" then
|
|
||||||
file = fs.concat(shell.workingDirectory, file)
|
|
||||||
end
|
|
||||||
if not fs.exists(file) then
|
|
||||||
print("\27[91mFile does not exist.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
fs.remove(file)
|
|
||||||
|
|||||||
+1
-91
@@ -1,91 +1 @@
|
|||||||
local raster = import("raster")
|
Error: no such file. (``)
|
||||||
|
|
||||||
raster.init()
|
|
||||||
|
|
||||||
--[[for i=4,20 do
|
|
||||||
raster.set(i,i)
|
|
||||||
raster.set(i,i+4,0xFF00FF)
|
|
||||||
end]]
|
|
||||||
|
|
||||||
--[[ for x=4,20 do
|
|
||||||
for y=4,20 do
|
|
||||||
if (x+y)%2==0 then
|
|
||||||
raster.set(x,y,0xFF00FF)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end ]]
|
|
||||||
|
|
||||||
local event = import("event")
|
|
||||||
local x=0
|
|
||||||
local y=0
|
|
||||||
local vx=1
|
|
||||||
local vy=1
|
|
||||||
local col = 0x808080
|
|
||||||
local i=0
|
|
||||||
|
|
||||||
while event.pull("key_down",0)==nil do
|
|
||||||
i = i + 1
|
|
||||||
raster.set(x,y,col)
|
|
||||||
|
|
||||||
x = x + vx
|
|
||||||
y = y + vy
|
|
||||||
|
|
||||||
if x>raster.displayWidth then
|
|
||||||
x=raster.displayWidth
|
|
||||||
vx = -math.abs(vx)
|
|
||||||
col = math.random(0,0xFFFFFF)
|
|
||||||
end
|
|
||||||
if x<1 then
|
|
||||||
x=1
|
|
||||||
vx = math.abs(vx)
|
|
||||||
col = math.random(0,0xFFFFFF)
|
|
||||||
end
|
|
||||||
if y>raster.displayHeight-6 then
|
|
||||||
y=raster.displayHeight-6
|
|
||||||
vy = -math.abs(vy)
|
|
||||||
col = math.random(0,0xFFFFFF)
|
|
||||||
end
|
|
||||||
if y<1 then
|
|
||||||
y=1
|
|
||||||
vy = math.abs(vy)
|
|
||||||
col = math.random(0,0xFFFFFF)
|
|
||||||
end
|
|
||||||
|
|
||||||
if i>10 and i%15>0 then
|
|
||||||
while true do
|
|
||||||
local tries=0
|
|
||||||
local dx,dy=math.random(1,raster.displayWidth),math.random(1,raster.displayHeight-6)
|
|
||||||
if raster.get(dx,dy)~=0 then
|
|
||||||
raster.set(dx,dy,0)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
tries = tries + 1
|
|
||||||
if tries>20 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if i%10==0 then
|
|
||||||
raster.update()
|
|
||||||
coroutine.yield()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[ for i=0,360,4 do
|
|
||||||
local angle = i/180*math.pi
|
|
||||||
if false then
|
|
||||||
local x1,y1,x2,y2=raster.displayWidth/2,raster.displayHeight/2,raster.displayWidth/2+math.sin(angle)*80,raster.displayHeight/2+math.cos(angle)*80
|
|
||||||
raster.fillEllipse(x1,y1,x2,y2,0xFF00FF)
|
|
||||||
raster.update()
|
|
||||||
raster.fillEllipse(x1,y1,x2,y2,0x000000)
|
|
||||||
else
|
|
||||||
local x,y,c=raster.displayWidth/2,raster.displayHeight/2,math.abs(math.sin(angle)*100)
|
|
||||||
raster.drawCircle(x,y,c,0xFF00FF)
|
|
||||||
raster.update()
|
|
||||||
raster.drawCircle(x,y,c,0x000000)
|
|
||||||
end
|
|
||||||
end ]]
|
|
||||||
|
|
||||||
raster.free()
|
|
||||||
termlib.cursorPosY=1
|
|
||||||
|
|||||||
+1
-141
@@ -1,141 +1 @@
|
|||||||
local component = import("component")
|
Error: no such file. (``)
|
||||||
local computer = import("computer")
|
|
||||||
local raster = import("raster")
|
|
||||||
local event = import("event")
|
|
||||||
|
|
||||||
-- Initialize the 3D renderer for a spinning cube
|
|
||||||
-- Using the raster library for drawing
|
|
||||||
|
|
||||||
-- Screen dimensions
|
|
||||||
local SCREEN_WIDTH, SCREEN_HEIGHT = component.invoke(component.list("gpu")(), "getResolution")
|
|
||||||
SCREEN_WIDTH, SCREEN_HEIGHT = SCREEN_WIDTH * 2, SCREEN_HEIGHT * 4
|
|
||||||
local CENTER_X = SCREEN_WIDTH / 2
|
|
||||||
local CENTER_Y = SCREEN_HEIGHT / 2
|
|
||||||
|
|
||||||
-- Cube properties
|
|
||||||
local CUBE_SIZE = 10
|
|
||||||
local increment = 0
|
|
||||||
local WHITE = 0xFFFFFF
|
|
||||||
local ROTATION_SPEED = 0.1
|
|
||||||
|
|
||||||
-- 3D cube vertices (centered at origin)
|
|
||||||
local vertices = {
|
|
||||||
{-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE}, -- 0: left bottom back
|
|
||||||
{CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE}, -- 1: right bottom back
|
|
||||||
{CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE}, -- 2: right top back
|
|
||||||
{-CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE}, -- 3: left top back
|
|
||||||
{-CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE}, -- 4: left bottom front
|
|
||||||
{CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE}, -- 5: right bottom front
|
|
||||||
{CUBE_SIZE, CUBE_SIZE, CUBE_SIZE}, -- 6: right top front
|
|
||||||
{-CUBE_SIZE, CUBE_SIZE, CUBE_SIZE} -- 7: left top front
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Cube edges defined by vertex indices
|
|
||||||
local edges = {
|
|
||||||
{0, 1}, {1, 2}, {2, 3}, {3, 0}, -- back face
|
|
||||||
{4, 5}, {5, 6}, {6, 7}, {7, 4}, -- front face
|
|
||||||
{0, 4}, {1, 5}, {2, 6}, {3, 7} -- connecting edges
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Projection parameters
|
|
||||||
local FOV = 256 -- Field of view (distance from camera to screen)
|
|
||||||
local Z_OFFSET = 300 -- Distance from camera to cube center
|
|
||||||
|
|
||||||
-- Initialize rotation angles
|
|
||||||
local angleX, angleY, angleZ = 0, 0, 0
|
|
||||||
|
|
||||||
-- Matrix multiplication function (apply rotation to a 3D point)
|
|
||||||
local function rotatePoint(x, y, z)
|
|
||||||
-- Rotation around X axis
|
|
||||||
local cosX, sinX = math.cos(angleX), math.sin(angleX)
|
|
||||||
local y1 = y * cosX - z * sinX
|
|
||||||
local z1 = y * sinX + z * cosX
|
|
||||||
|
|
||||||
-- Rotation around Y axis
|
|
||||||
local cosY, sinY = math.cos(angleY), math.sin(angleY)
|
|
||||||
local x1 = x * cosY + z1 * sinY
|
|
||||||
local z2 = -x * sinY + z1 * cosY
|
|
||||||
|
|
||||||
-- Rotation around Z axis
|
|
||||||
local cosZ, sinZ = math.cos(angleZ), math.sin(angleZ)
|
|
||||||
local x2 = x1 * cosZ - y1 * sinZ
|
|
||||||
local y2 = x1 * sinZ + y1 * cosZ
|
|
||||||
|
|
||||||
return x2, y2, z2
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Perspective projection function (3D to 2D)
|
|
||||||
local function projectPoint(x, y, z)
|
|
||||||
-- Apply perspective projection
|
|
||||||
local scale = FOV / (z + Z_OFFSET)
|
|
||||||
local x2d = x * scale + CENTER_X
|
|
||||||
local y2d = y * scale + CENTER_Y
|
|
||||||
|
|
||||||
return x2d, y2d
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Render a single frame
|
|
||||||
local function renderFrame()
|
|
||||||
local time = computer.uptime()*20
|
|
||||||
increment = time*0.05 -- increment + 0.05
|
|
||||||
CUBE_SIZE = (math.sin(increment) + 1) * 25
|
|
||||||
vertices = {
|
|
||||||
{-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE}, -- 0: left bottom back
|
|
||||||
{CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE}, -- 1: right bottom back
|
|
||||||
{CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE}, -- 2: right top back
|
|
||||||
{-CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE}, -- 3: left top back
|
|
||||||
{-CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE}, -- 4: left bottom front
|
|
||||||
{CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE}, -- 5: right bottom front
|
|
||||||
{CUBE_SIZE, CUBE_SIZE, CUBE_SIZE}, -- 6: right top front
|
|
||||||
{-CUBE_SIZE, CUBE_SIZE, CUBE_SIZE} -- 7: left top front
|
|
||||||
}
|
|
||||||
-- Update rotation angles
|
|
||||||
raster.clear()
|
|
||||||
angleX = time * ROTATION_SPEED -- angleX
|
|
||||||
angleY = time * ROTATION_SPEED * 0.7 -- angleY
|
|
||||||
angleZ = time * ROTATION_SPEED * 0.5 -- angleZ
|
|
||||||
|
|
||||||
-- Project all vertices
|
|
||||||
local projectedPoints = {}
|
|
||||||
for i, vertex in ipairs(vertices) do
|
|
||||||
-- Rotate the point
|
|
||||||
local x, y, z = rotatePoint(vertex[1], vertex[2], vertex[3])
|
|
||||||
|
|
||||||
-- Project the point to 2D
|
|
||||||
local x2d, y2d = projectPoint(x, y, z)
|
|
||||||
projectedPoints[i] = {x2d, y2d}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Draw all edges
|
|
||||||
for _, edge in ipairs(edges) do
|
|
||||||
local p1 = projectedPoints[edge[1] + 1] -- +1 because Lua indices start at 1
|
|
||||||
local p2 = projectedPoints[edge[2] + 1]
|
|
||||||
|
|
||||||
-- Draw the line
|
|
||||||
raster.drawLine(p1[1], p1[2], p2[1], p2[2], WHITE)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Render the frame
|
|
||||||
raster.update()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Main program
|
|
||||||
function main()
|
|
||||||
-- Initialize raster engine
|
|
||||||
raster.init()
|
|
||||||
|
|
||||||
-- Main loop (assume this is called repeatedly by the host environment)
|
|
||||||
while true do
|
|
||||||
renderFrame()
|
|
||||||
coroutine.yield()
|
|
||||||
if event.pull("key_down", 0) then
|
|
||||||
raster.free()
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- Return a reference to renderFrame so it can be called for animation
|
|
||||||
return renderFrame
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Start the program
|
|
||||||
return main()
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
import("computer").shutdown()
|
Error: no such file. (``)
|
||||||
|
|||||||
+1
-98
@@ -1,98 +1 @@
|
|||||||
local loadfile = ...
|
Error: no such file. (``)
|
||||||
local filesystem = loadfile("/halyde/lib/filesystem.lua")(loadfile)
|
|
||||||
|
|
||||||
_G._OSVERSION = "Halyde 2.8.1"
|
|
||||||
_G._OSLOGO = ""
|
|
||||||
local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge)
|
|
||||||
_OSLOGO = _OSLOGO .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
|
|
||||||
_G.package = {["preloaded"] = {}}
|
|
||||||
|
|
||||||
loadfile("/halyde/core/datatools.lua")()
|
|
||||||
|
|
||||||
function _G.import(module, ...)
|
|
||||||
local args = table.pack(...)
|
|
||||||
if package.preloaded[module] then
|
|
||||||
return package.preloaded[module]
|
|
||||||
end
|
|
||||||
local modulepath
|
|
||||||
if filesystem.exists(module) then
|
|
||||||
modulepath = module
|
|
||||||
elseif filesystem.exists("/halyde/lib/"..module..".lua") then
|
|
||||||
modulepath = "/halyde/lib/"..module..".lua"
|
|
||||||
elseif shell and shell.workingDirectory and filesystem.exists(shell.workingDirectory..module) then
|
|
||||||
modulepath = shell.workingDirectory..module
|
|
||||||
end
|
|
||||||
assert(modulepath, "module not found\npossible locations:\n/halyde/lib/"..module..".lua")
|
|
||||||
local handle, data, tmpdata = filesystem.open(modulepath), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
handle:close()
|
|
||||||
return(assert(load(data, "="..modulepath))(table.unpack(args)))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function preload(module)
|
|
||||||
local handle, data, tmpdata = assert(filesystem.open("/halyde/lib/" .. module .. ".lua", "r")), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
package.preloaded[module] = assert(load(data, "="..module))()
|
|
||||||
_G[module] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
preload("component")
|
|
||||||
preload("computer")
|
|
||||||
preload("filesystem")
|
|
||||||
|
|
||||||
local component = import("component")
|
|
||||||
local gpu = component.gpu
|
|
||||||
local screenAddress = component.list("screen")()
|
|
||||||
--local screen = component.screen
|
|
||||||
|
|
||||||
gpu.bind(screenAddress)
|
|
||||||
--local maxWidth, maxHeight = gpu.maxResolution()
|
|
||||||
--local aspectX, aspectY = screen.getAspectRatio()
|
|
||||||
--local screenRatio = aspectX * 2 / aspectY
|
|
||||||
|
|
||||||
-- Calculate potential dimensions
|
|
||||||
--local widthLimited = math.floor(maxHeight * screenRatio)
|
|
||||||
--local heightLimited = math.floor(maxWidth / screenRatio)
|
|
||||||
|
|
||||||
--local targetWidth, targetHeight
|
|
||||||
|
|
||||||
--if widthLimited <= maxWidth then
|
|
||||||
-- height is the limiting factor
|
|
||||||
-- targetWidth = widthLimited
|
|
||||||
-- targetHeight = maxHeight
|
|
||||||
--else
|
|
||||||
-- width is the limiting factor
|
|
||||||
-- targetWidth = maxWidth
|
|
||||||
-- targetHeight = heightLimited
|
|
||||||
--end
|
|
||||||
|
|
||||||
--targetWidth = math.min(targetWidth, maxWidth)
|
|
||||||
--targetHeight = math.min(targetHeight, maxHeight)
|
|
||||||
|
|
||||||
--gpu.setResolution(targetWidth, targetHeight)
|
|
||||||
gpu.setResolution(gpu.maxResolution())
|
|
||||||
|
|
||||||
--local handle = assert(filesystem.open("/bazinga.txt", "w"))
|
|
||||||
--assert(handle:write("Bazinga!"))
|
|
||||||
--handle:close()
|
|
||||||
|
|
||||||
local fs = import("filesystem")
|
|
||||||
if not fs.exists("/halyde/config/shell.json") then -- Auto-generate configs
|
|
||||||
fs.copy("/halyde/config/generate/shell.json", "/halyde/config/shell.json")
|
|
||||||
end
|
|
||||||
if not fs.exists("/halyde/config/startupapps.json") then
|
|
||||||
fs.copy("/halyde/config/generate/startupapps.json", "/halyde/config/startupapps.json")
|
|
||||||
end
|
|
||||||
fs = nil
|
|
||||||
|
|
||||||
import("/halyde/core/cormgr.lua")
|
|
||||||
|
|||||||
+1
-97
@@ -1,97 +1 @@
|
|||||||
_G.cormgr = {}
|
Error: no such file. (``)
|
||||||
_G.cormgr.corList = {}
|
|
||||||
_G.cormgr.labelList = {}
|
|
||||||
|
|
||||||
local component = import("component")
|
|
||||||
local filesystem = import("filesystem")
|
|
||||||
local json = import("json")
|
|
||||||
local gpu = component.gpu
|
|
||||||
--local ocelot = component.ocelot
|
|
||||||
|
|
||||||
function _G.cormgr.loadCoroutine(path,...)
|
|
||||||
local args = {...}
|
|
||||||
local function corFunction()
|
|
||||||
local result, errorMessage = xpcall(function(...)
|
|
||||||
import(...)
|
|
||||||
end, function(errorMessage)
|
|
||||||
return errorMessage .. "\n \n" .. debug.traceback()
|
|
||||||
end, path, table.unpack(args))
|
|
||||||
if not result then
|
|
||||||
if print then
|
|
||||||
gpu.freeAllBuffers()
|
|
||||||
print("\n\27[91m" .. errorMessage)
|
|
||||||
else
|
|
||||||
error(errorMessage)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--import(path, table.unpack(args))
|
|
||||||
end
|
|
||||||
cormgr.addCoroutine(corFunction, string.match(tostring(path), "([^/]+)%.lua$"))
|
|
||||||
end
|
|
||||||
|
|
||||||
function _G.cormgr.addCoroutine(func, name)
|
|
||||||
local cor = coroutine.create(func)
|
|
||||||
table.insert(cormgr.corList, cor)
|
|
||||||
table.insert(cormgr.labelList, name)
|
|
||||||
return cor
|
|
||||||
end
|
|
||||||
|
|
||||||
function _G.cormgr.removeCoroutine(name)
|
|
||||||
local index = table.find(cormgr.labelList, cor)
|
|
||||||
table.remove(cormgr.corList, index)
|
|
||||||
table.remove(cormgr.labelList, index)
|
|
||||||
--coroutine.close(cor)
|
|
||||||
end
|
|
||||||
|
|
||||||
function handleError(errormsg)
|
|
||||||
if errormsg == nil then
|
|
||||||
error("unknown error")
|
|
||||||
else
|
|
||||||
error(tostring(errormsg).."\n \n"..debug.traceback())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function runCoroutines()
|
|
||||||
for i = 1, #_G.cormgr.corList do
|
|
||||||
if cormgr.corList[i] then
|
|
||||||
local result, errorMessage = coroutine.resume(cormgr.corList[i])
|
|
||||||
if cormgr.corList[i] then
|
|
||||||
if not result then
|
|
||||||
handleError(errorMessage)
|
|
||||||
end
|
|
||||||
if coroutine.status(cormgr.corList[i]) == "dead" then
|
|
||||||
table.remove(cormgr.corList, i)
|
|
||||||
table.remove(cormgr.labelList, i)
|
|
||||||
--ocelot.log("Removed coroutine")
|
|
||||||
i = i - 1
|
|
||||||
end
|
|
||||||
--computer.pullSignal(0)
|
|
||||||
--coroutine.yield()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local handle, data, tmpdata = filesystem.open("/halyde/config/startupapps.json", "r"), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
handle:close()
|
|
||||||
for _, line in ipairs(json.decode(data)) do
|
|
||||||
if line ~= "" then
|
|
||||||
--[[ if _G.print then
|
|
||||||
print(line)
|
|
||||||
end ]]
|
|
||||||
_G.cormgr.loadCoroutine(line)
|
|
||||||
runCoroutines()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- _G.cormgr.loadCoroutine("/halyde/core/shell.lua")
|
|
||||||
|
|
||||||
while true do
|
|
||||||
runCoroutines()
|
|
||||||
if #_G.cormgr.corList == 0 then
|
|
||||||
computer.shutdown()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -1,45 +1 @@
|
|||||||
local conversionTables = {
|
Error: no such file. (``)
|
||||||
["bytes"] = {
|
|
||||||
["B"] = 1,
|
|
||||||
["KB"] = 1000,
|
|
||||||
["MB"] = 1000000,
|
|
||||||
["GB"] = 1000000000
|
|
||||||
}, ["bibytes"] = {
|
|
||||||
["B"] = 1,
|
|
||||||
["KiB"] = 1024,
|
|
||||||
["MiB"] = 1048576,
|
|
||||||
["GiB"] = 1073741824
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function table.find(tab, item)
|
|
||||||
for k, v in pairs(tab) do
|
|
||||||
if v == item then
|
|
||||||
return k
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function table.copy(orig)
|
|
||||||
local orig_type = type(orig)
|
|
||||||
local copy
|
|
||||||
if orig_type == 'table' then
|
|
||||||
copy = {}
|
|
||||||
for orig_key, orig_value in next, orig, nil do
|
|
||||||
copy[table.copy(orig_key)] = table.copy(orig_value)
|
|
||||||
end
|
|
||||||
setmetatable(copy, table.copy(getmetatable(orig)))
|
|
||||||
else -- number, string, boolean, etc
|
|
||||||
copy = orig
|
|
||||||
end
|
|
||||||
return copy
|
|
||||||
end
|
|
||||||
|
|
||||||
function convert(amount, fromUnit, toUnit)
|
|
||||||
for _, convTable in pairs(conversionTables) do
|
|
||||||
if convTable[toUnit] then
|
|
||||||
return amount / convTable[toUnit] * convTable[fromUnit]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false, "unit does not exist"
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-46
@@ -1,46 +1 @@
|
|||||||
local fs = import("filesystem")
|
Error: no such file. (``)
|
||||||
|
|
||||||
local driverPath = "/halyde/drivers"
|
|
||||||
|
|
||||||
local drivers = fs.list(driverPath)
|
|
||||||
local driverTypes = {}
|
|
||||||
|
|
||||||
local function loadDriver(drvName)
|
|
||||||
local driverData = import(fs.concat(driverPath, drvName))
|
|
||||||
table.remove(drivers, table.find(drivers, drvName))
|
|
||||||
if driverData.dependencies then
|
|
||||||
for _, dependency in pairs(driverData.dependencies) do
|
|
||||||
if table.find(drivers, dependency) then
|
|
||||||
loadDriver(dependency)
|
|
||||||
elseif table.find(drivers, dependency .. ".lua") then
|
|
||||||
loadDriver(dependency .. ".lua")
|
|
||||||
else
|
|
||||||
for typeLookupDrvName, typeLookupDrvType in pairs(driverTypes) do
|
|
||||||
if typeLookupDrvType == dependency then
|
|
||||||
loadDriver(typeLookupDrvName)
|
|
||||||
-- Don't break, because there can be multiple drivers of the correct type
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--print(drvName)
|
|
||||||
if driverData.onStartup then -- I have no idea why would this not exist, but it's a failsafe
|
|
||||||
driverData.onStartup()
|
|
||||||
end
|
|
||||||
-- More functions to be implemented in the future
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, drvName in pairs(drivers) do -- Get all the driver types
|
|
||||||
local driverData = import(fs.concat(driverPath, drvName))
|
|
||||||
if driverData.type then
|
|
||||||
--print(driverData.type)
|
|
||||||
driverTypes[drvName] = driverData.type -- Not the other way around because there can be multiple drivers of the same type, but there can't be multiple entries with the same key
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, drvName in pairs(drivers) do -- Load the drivers
|
|
||||||
if drvName:sub(-1, -1) ~= "/" then -- Check if it's not a directory. Otherwise it might be driver config
|
|
||||||
loadDriver(drvName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-55
@@ -1,55 +1 @@
|
|||||||
_G.evmgr = {}
|
Error: no such file. (``)
|
||||||
_G.evmgr.eventQueue = {}
|
|
||||||
local maxEventQueueLength = 10 -- increase if events start getting dropped
|
|
||||||
|
|
||||||
local computer = import("computer")
|
|
||||||
|
|
||||||
keyboard.ctrlDown = false
|
|
||||||
keyboard.altDown = false
|
|
||||||
keyboard.shiftDown = false
|
|
||||||
|
|
||||||
--local ocelot = component.proxy(component.list("ocelot")())
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local args
|
|
||||||
repeat
|
|
||||||
args = {computer.uptime(), computer.pullSignal(0)}
|
|
||||||
if args and args[2] then
|
|
||||||
table.insert(evmgr.eventQueue, args)
|
|
||||||
if keyboard then
|
|
||||||
if args[2] == "key_down" then
|
|
||||||
local keycode = args[5]
|
|
||||||
local key = keyboard.keys[keycode]
|
|
||||||
if key == "lcontrol" then
|
|
||||||
keyboard.ctrlDown = true
|
|
||||||
elseif key == "lmenu" then
|
|
||||||
keyboard.altDown = true
|
|
||||||
elseif key == "lshift" then
|
|
||||||
keyboard.shiftDown = true
|
|
||||||
elseif key == "c" and keyboard.ctrlDown and keyboard.altDown then
|
|
||||||
if print then
|
|
||||||
print("\n\27[91mCoroutine "..tostring(#cormgr.corList).." killed.")
|
|
||||||
end
|
|
||||||
cormgr.corList[#cormgr.corList] = nil
|
|
||||||
end
|
|
||||||
elseif args[2] == "key_up" then
|
|
||||||
local keycode = args[5]
|
|
||||||
local key = keyboard.keys[keycode]
|
|
||||||
if key == "lcontrol" then
|
|
||||||
keyboard.ctrlDown = false
|
|
||||||
elseif key == "lmenu" then
|
|
||||||
keyboard.altDown = false
|
|
||||||
elseif key == "lshift" then
|
|
||||||
keyboard.shiftDown = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while #evmgr.eventQueue > maxEventQueueLength do
|
|
||||||
--ocelot.log("Queue length breach, removing first signal")
|
|
||||||
table.remove(evmgr.eventQueue, 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
until not args or not args[1]
|
|
||||||
--ocelot.log("done")
|
|
||||||
coroutine.yield()
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-143
@@ -1,143 +1 @@
|
|||||||
_G.keyboard = {["keys"] = {}}
|
Error: no such file. (``)
|
||||||
|
|
||||||
keyboard.keys["1"] = 0x02
|
|
||||||
keyboard.keys["2"] = 0x03
|
|
||||||
keyboard.keys["3"] = 0x04
|
|
||||||
keyboard.keys["4"] = 0x05
|
|
||||||
keyboard.keys["5"] = 0x06
|
|
||||||
keyboard.keys["6"] = 0x07
|
|
||||||
keyboard.keys["7"] = 0x08
|
|
||||||
keyboard.keys["8"] = 0x09
|
|
||||||
keyboard.keys["9"] = 0x0A
|
|
||||||
keyboard.keys["0"] = 0x0B
|
|
||||||
keyboard.keys.a = 0x1E
|
|
||||||
keyboard.keys.b = 0x30
|
|
||||||
keyboard.keys.c = 0x2E
|
|
||||||
keyboard.keys.d = 0x20
|
|
||||||
keyboard.keys.e = 0x12
|
|
||||||
keyboard.keys.f = 0x21
|
|
||||||
keyboard.keys.g = 0x22
|
|
||||||
keyboard.keys.h = 0x23
|
|
||||||
keyboard.keys.i = 0x17
|
|
||||||
keyboard.keys.j = 0x24
|
|
||||||
keyboard.keys.k = 0x25
|
|
||||||
keyboard.keys.l = 0x26
|
|
||||||
keyboard.keys.m = 0x32
|
|
||||||
keyboard.keys.n = 0x31
|
|
||||||
keyboard.keys.o = 0x18
|
|
||||||
keyboard.keys.p = 0x19
|
|
||||||
keyboard.keys.q = 0x10
|
|
||||||
keyboard.keys.r = 0x13
|
|
||||||
keyboard.keys.s = 0x1F
|
|
||||||
keyboard.keys.t = 0x14
|
|
||||||
keyboard.keys.u = 0x16
|
|
||||||
keyboard.keys.v = 0x2F
|
|
||||||
keyboard.keys.w = 0x11
|
|
||||||
keyboard.keys.x = 0x2D
|
|
||||||
keyboard.keys.y = 0x15
|
|
||||||
keyboard.keys.z = 0x2C
|
|
||||||
|
|
||||||
keyboard.keys.apostrophe = 0x28
|
|
||||||
keyboard.keys.at = 0x91
|
|
||||||
keyboard.keys.back = 0x0E -- backspace
|
|
||||||
keyboard.keys.backslash = 0x2B
|
|
||||||
keyboard.keys.capital = 0x3A -- capslock
|
|
||||||
keyboard.keys.colon = 0x92
|
|
||||||
keyboard.keys.comma = 0x33
|
|
||||||
keyboard.keys.enter = 0x1C
|
|
||||||
keyboard.keys.equals = 0x0D
|
|
||||||
keyboard.keys.grave = 0x29 -- accent grave
|
|
||||||
keyboard.keys.lbracket = 0x1A
|
|
||||||
keyboard.keys.lcontrol = 0x1D
|
|
||||||
keyboard.keys.lmenu = 0x38 -- left Alt
|
|
||||||
keyboard.keys.lshift = 0x2A
|
|
||||||
keyboard.keys.minus = 0x0C
|
|
||||||
keyboard.keys.numlock = 0x45
|
|
||||||
keyboard.keys.pause = 0xC5
|
|
||||||
keyboard.keys.period = 0x34
|
|
||||||
keyboard.keys.rbracket = 0x1B
|
|
||||||
keyboard.keys.rcontrol = 0x9D
|
|
||||||
keyboard.keys.rmenu = 0xB8 -- right Alt
|
|
||||||
keyboard.keys.rshift = 0x36
|
|
||||||
keyboard.keys.scroll = 0x46 -- Scroll Lock
|
|
||||||
keyboard.keys.semicolon = 0x27
|
|
||||||
keyboard.keys.slash = 0x35 -- / on main keyboard
|
|
||||||
keyboard.keys.space = 0x39
|
|
||||||
keyboard.keys.stop = 0x95
|
|
||||||
keyboard.keys.tab = 0x0F
|
|
||||||
keyboard.keys.underline = 0x93
|
|
||||||
|
|
||||||
-- Keypad (and numpad with numlock off)
|
|
||||||
keyboard.keys.up = 0xC8
|
|
||||||
keyboard.keys.down = 0xD0
|
|
||||||
keyboard.keys.left = 0xCB
|
|
||||||
keyboard.keys.right = 0xCD
|
|
||||||
keyboard.keys.home = 0xC7
|
|
||||||
keyboard.keys["end"] = 0xCF
|
|
||||||
keyboard.keys.pageUp = 0xC9
|
|
||||||
keyboard.keys.pageDown = 0xD1
|
|
||||||
keyboard.keys.insert = 0xD2
|
|
||||||
keyboard.keys.delete = 0xD3
|
|
||||||
|
|
||||||
-- Function keys
|
|
||||||
keyboard.keys.f1 = 0x3B
|
|
||||||
keyboard.keys.f2 = 0x3C
|
|
||||||
keyboard.keys.f3 = 0x3D
|
|
||||||
keyboard.keys.f4 = 0x3E
|
|
||||||
keyboard.keys.f5 = 0x3F
|
|
||||||
keyboard.keys.f6 = 0x40
|
|
||||||
keyboard.keys.f7 = 0x41
|
|
||||||
keyboard.keys.f8 = 0x42
|
|
||||||
keyboard.keys.f9 = 0x43
|
|
||||||
keyboard.keys.f10 = 0x44
|
|
||||||
keyboard.keys.f11 = 0x57
|
|
||||||
keyboard.keys.f12 = 0x58
|
|
||||||
keyboard.keys.f13 = 0x64
|
|
||||||
keyboard.keys.f14 = 0x65
|
|
||||||
keyboard.keys.f15 = 0x66
|
|
||||||
keyboard.keys.f16 = 0x67
|
|
||||||
keyboard.keys.f17 = 0x68
|
|
||||||
keyboard.keys.f18 = 0x69
|
|
||||||
keyboard.keys.f19 = 0x71
|
|
||||||
|
|
||||||
-- Japanese keyboards
|
|
||||||
keyboard.keys.kana = 0x70
|
|
||||||
keyboard.keys.kanji = 0x94
|
|
||||||
keyboard.keys.convert = 0x79
|
|
||||||
keyboard.keys.noconvert = 0x7B
|
|
||||||
keyboard.keys.yen = 0x7D
|
|
||||||
keyboard.keys.circumflex = 0x90
|
|
||||||
keyboard.keys.ax = 0x96
|
|
||||||
|
|
||||||
-- Numpad
|
|
||||||
keyboard.keys.numpad0 = 0x52
|
|
||||||
keyboard.keys.numpad1 = 0x4F
|
|
||||||
keyboard.keys.numpad2 = 0x50
|
|
||||||
keyboard.keys.numpad3 = 0x51
|
|
||||||
keyboard.keys.numpad4 = 0x4B
|
|
||||||
keyboard.keys.numpad5 = 0x4C
|
|
||||||
keyboard.keys.numpad6 = 0x4D
|
|
||||||
keyboard.keys.numpad7 = 0x47
|
|
||||||
keyboard.keys.numpad8 = 0x48
|
|
||||||
keyboard.keys.numpad9 = 0x49
|
|
||||||
keyboard.keys.numpadmul = 0x37
|
|
||||||
keyboard.keys.numpaddiv = 0xB5
|
|
||||||
keyboard.keys.numpadsub = 0x4A
|
|
||||||
keyboard.keys.numpadadd = 0x4E
|
|
||||||
keyboard.keys.numpaddecimal = 0x53
|
|
||||||
keyboard.keys.numpadcomma = 0xB3
|
|
||||||
keyboard.keys.numpadenter = 0x9C
|
|
||||||
keyboard.keys.numpadequals = 0x8D
|
|
||||||
|
|
||||||
-- Create inverse mapping for name lookup.
|
|
||||||
setmetatable(keyboard.keys,
|
|
||||||
{
|
|
||||||
__index = function(tbl, k)
|
|
||||||
if type(k) ~= "number" then return end
|
|
||||||
for name,value in pairs(tbl) do
|
|
||||||
if value == k then
|
|
||||||
return name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|||||||
+1
-106
@@ -1,106 +1 @@
|
|||||||
local fs = import("filesystem")
|
Error: no such file. (``)
|
||||||
local json = import("json")
|
|
||||||
local handle, data, tmpdata = fs.open("/halyde/config/shell.json", "r"), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
handle:close()
|
|
||||||
local shellcfg = json.decode(data)
|
|
||||||
import("/halyde/core/termlib.lua")
|
|
||||||
local event = import("event")
|
|
||||||
local component = import("component")
|
|
||||||
local gpu = component.gpu
|
|
||||||
|
|
||||||
_G.shell = {}
|
|
||||||
_G.shell.workingDirectory = shellcfg["defaultWorkingDirectory"]
|
|
||||||
_G.shell.aliases = shellcfg["aliases"]
|
|
||||||
|
|
||||||
local function runAsCoroutine(path, ...)
|
|
||||||
--ocelot.log("running " .. path .. " as coroutine")
|
|
||||||
cormgr.loadCoroutine(path, ...)
|
|
||||||
local corIndex = #cormgr.corList
|
|
||||||
local cor = cormgr.corList[#cormgr.corList]
|
|
||||||
repeat
|
|
||||||
coroutine.yield()
|
|
||||||
until cormgr.corList[corIndex] ~= cor
|
|
||||||
end
|
|
||||||
|
|
||||||
function _G.shell.run(command)
|
|
||||||
checkArg(1, command, "string")
|
|
||||||
if shell.aliases[command:match("[^ ]+")] then
|
|
||||||
local _, cmdend = command:find("[^ ]+")
|
|
||||||
command = shell.aliases[command:match("[^ ]+")] .. command:sub(cmdend + 1)
|
|
||||||
end
|
|
||||||
local gm, result, args, trimmedCommand = command:gmatch("[^ ]+"), nil, {}, command
|
|
||||||
while true do
|
|
||||||
result = gm()
|
|
||||||
if not result then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
if result:find('"') then
|
|
||||||
local location = trimmedCommand:find('"')
|
|
||||||
local argBefore = result:sub(1, result:find('"') - 1) -- edge case where there is no space before the quote, get the argument there
|
|
||||||
if argBefore and argBefore ~= "" then
|
|
||||||
table.insert(args, argBefore)
|
|
||||||
end
|
|
||||||
trimmedCommand = trimmedCommand:sub(location + 1)
|
|
||||||
if trimmedCommand:find('"') then
|
|
||||||
table.insert(args, trimmedCommand:sub(1, trimmedCommand:find('"') - 1))
|
|
||||||
trimmedCommand = trimmedCommand:sub(trimmedCommand:find('"') + 1)
|
|
||||||
gm = trimmedCommand:gmatch('[^ ]+')
|
|
||||||
else
|
|
||||||
print("\27[91mmalformed shell command")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.insert(args, result)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- execute the program
|
|
||||||
local PATH = table.copy(shellcfg.path)
|
|
||||||
table.insert(PATH, shell.workingDirectory)
|
|
||||||
if not args[1] then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if fs.exists(args[1]) and not fs.isDirectory(args[1]) then
|
|
||||||
local path = args[1]
|
|
||||||
table.remove(args, 1)
|
|
||||||
runAsCoroutine(path, table.unpack(args))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
for _, item in pairs(PATH) do
|
|
||||||
if fs.exists(item..args[1]) and not fs.isDirectory(item .. args[1]) then
|
|
||||||
local path = fs.concat(item, args[1])
|
|
||||||
table.remove(args, 1)
|
|
||||||
runAsCoroutine(path, table.unpack(args))
|
|
||||||
return
|
|
||||||
else -- try to look for it without the file extension
|
|
||||||
local files = fs.list(item) or {}
|
|
||||||
for _, file in pairs(files) do
|
|
||||||
-- previous pattern: (.+)%.[^%.]+$
|
|
||||||
if args[1] == file:match("(.+)%.[^%.]+$") and not fs.isDirectory(item .. file) then
|
|
||||||
table.remove(args, 1)
|
|
||||||
runAsCoroutine(item .. file, table.unpack(args))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print("No such file or command: "..args[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
print(shellcfg["startupMessage"]:format(_OSVERSION, shellcfg.splashMessages[math.random(1, #shellcfg.splashMessages)]))
|
|
||||||
while true do
|
|
||||||
coroutine.yield()
|
|
||||||
-- print(shell.workingDirectory .. " >")
|
|
||||||
--print(shellcfg["prompt"]:format(shell.workingDirectory),false)
|
|
||||||
-- termlib.cursorPosX = #(shell.workingDirectory .. " > ")
|
|
||||||
-- termlib.cursorPosY = termlib.cursorPosY - 1
|
|
||||||
if shell.workingDirectory:sub(-1, -1) ~= "/" then
|
|
||||||
shell.workingDirectory = shell.workingDirectory .. "/"
|
|
||||||
end
|
|
||||||
local shellCommand = read("shell", shellcfg.prompt:format(shell.workingDirectory))
|
|
||||||
shell.run(shellCommand)
|
|
||||||
gpu.freeAllBuffers()
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-453
@@ -1,453 +1 @@
|
|||||||
local serialize = import("serialize")
|
Error: no such file. (``)
|
||||||
local unicode = import("unicode")
|
|
||||||
local event = import("event")
|
|
||||||
--local keyboard = import("keyboard")
|
|
||||||
|
|
||||||
--local ocelot = component.proxy(component.list("ocelot")())
|
|
||||||
local component = import("component")
|
|
||||||
local computer = import("computer")
|
|
||||||
local gpu = component.gpu
|
|
||||||
_G.termlib = {}
|
|
||||||
termlib.cursorPosX = 1
|
|
||||||
termlib.cursorPosY = 1
|
|
||||||
termlib.readHistory = {}
|
|
||||||
|
|
||||||
local width, height = gpu.getResolution()
|
|
||||||
|
|
||||||
local ANSIColorPalette = {
|
|
||||||
["dark"] = {
|
|
||||||
[0] = 0x000000,
|
|
||||||
[1] = 0x800000,
|
|
||||||
[2] = 0x008000,
|
|
||||||
[3] = 0x808000,
|
|
||||||
[4] = 0x000080,
|
|
||||||
[5] = 0x800080,
|
|
||||||
[6] = 0x008080,
|
|
||||||
[7] = 0xC0C0C0
|
|
||||||
},
|
|
||||||
["bright"] = {
|
|
||||||
[0] = 0x808080,
|
|
||||||
[1] = 0xFF0000,
|
|
||||||
[2] = 0x00FF00,
|
|
||||||
[3] = 0xFFFF00,
|
|
||||||
[4] = 0x0000FF,
|
|
||||||
[5] = 0xFF00FF,
|
|
||||||
[6] = 0x00FFFF,
|
|
||||||
[7] = 0xFFFFFF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultForegroundColor = ANSIColorPalette["bright"][7]
|
|
||||||
defaultBackgroundColor = ANSIColorPalette["dark"][0]
|
|
||||||
|
|
||||||
gpu.setForeground(defaultForegroundColor)
|
|
||||||
gpu.setBackground(defaultBackgroundColor)
|
|
||||||
|
|
||||||
local function scrollDown()
|
|
||||||
local width, height = gpu.getResolution()
|
|
||||||
if gpu.copy(1,1,width,height,0,-1) then
|
|
||||||
local prevForeground = gpu.getForeground()
|
|
||||||
local prevBackground = gpu.getBackground()
|
|
||||||
gpu.setForeground(defaultForegroundColor)
|
|
||||||
gpu.setBackground(defaultBackgroundColor)
|
|
||||||
gpu.fill(1, height, width, 1, " ")
|
|
||||||
gpu.setForeground(prevForeground)
|
|
||||||
gpu.setBackground(prevBackground)
|
|
||||||
termlib.cursorPosY=height
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function newLine()
|
|
||||||
termlib.cursorPosX=1
|
|
||||||
termlib.cursorPosY = termlib.cursorPosY + 1
|
|
||||||
if termlib.cursorPosY>height then
|
|
||||||
scrollDown()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function parseCodeNumbers(code)
|
|
||||||
local o = {}
|
|
||||||
for num in code:sub(3,-2):gmatch("[^;]+") do
|
|
||||||
table.insert(o,tonumber(num))
|
|
||||||
end
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
local function from8BitColor(num)
|
|
||||||
num=math.floor(num)&255
|
|
||||||
if num<16 then return 0x444444*((num>>3)&1)+(0xBB0000*((num>>2)&1)|0x00BB00*((num>>1)&1)|0x0000BB*(num&1)) end
|
|
||||||
if num>=232 then return 0x10101*(8+(num-232)*10) end
|
|
||||||
num=num-16
|
|
||||||
local palette = {0,95,135,175,215,255}
|
|
||||||
return (palette[(num//36)%6+1]<<16)|(palette[(num//6)%6+1]<<8)|palette[num%6+1]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function from24BitColor(r,g,b)
|
|
||||||
r,g,b=math.floor(r)&255,math.floor(g)&255,math.floor(b)&255
|
|
||||||
return (r<<16)|(g<<8)|b
|
|
||||||
end
|
|
||||||
|
|
||||||
local function findCodeEnd(text,i)
|
|
||||||
local function inRange(v,min,max)
|
|
||||||
return v>=min and v<=max
|
|
||||||
end
|
|
||||||
i=i+2
|
|
||||||
while i<=#text and not inRange(text:byte(i),0x40,0x7F) do i=i+1 end
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
|
|
||||||
function termlib.write(text, textWrap)
|
|
||||||
local width, height = gpu.getResolution()
|
|
||||||
|
|
||||||
-- you don't know how tiring this was just for ANSI escape code support
|
|
||||||
|
|
||||||
if textWrap == nil then
|
|
||||||
textWrap = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if not text or not tostring(text) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if text:find("\a") then
|
|
||||||
computer.beep()
|
|
||||||
end
|
|
||||||
text = tostring(text)
|
|
||||||
text = "\27[0m" .. text:gsub("\t", " ")
|
|
||||||
local readBreak = 0
|
|
||||||
-- readBreak is for when, inside the for loop, there normally would have been an increase in the "i" variable because it has read more than one character.
|
|
||||||
-- unfortunately, changing the "i" variable would have unpredictable effects, so to not risk anything, this workaround was done.
|
|
||||||
local section = ""
|
|
||||||
|
|
||||||
local function printSection()
|
|
||||||
if #section==0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
while true do
|
|
||||||
gpu.set(termlib.cursorPosX,termlib.cursorPosY,section)
|
|
||||||
if unicode.wlen(section) > width - termlib.cursorPosX + 1 and textWrap then
|
|
||||||
section = section:sub(width - termlib.cursorPosX + 2)
|
|
||||||
newLine()
|
|
||||||
else
|
|
||||||
termlib.cursorPosX = termlib.cursorPosX+unicode.wlen(section)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
section = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=1,#text do
|
|
||||||
if readBreak>0 then
|
|
||||||
readBreak = readBreak - 1
|
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
|
|
||||||
if string.byte(text,i)==10 then
|
|
||||||
printSection()
|
|
||||||
newLine()
|
|
||||||
elseif string.byte(text,i)==13 then
|
|
||||||
printSection()
|
|
||||||
termlib.cursorPosX=1
|
|
||||||
elseif string.byte(text,i)==0x1b and i<=#text-2 then
|
|
||||||
printSection()
|
|
||||||
--ocelot.log("0x1b char detected")
|
|
||||||
local codeType = string.sub(text,i+1,i+1)
|
|
||||||
if codeType=="[" then
|
|
||||||
-- Control Sequence Introducer
|
|
||||||
--ocelot.log("Control Sequence Introducer")
|
|
||||||
local codeEndIdx = findCodeEnd(text,i)
|
|
||||||
-- codeEndIdx = string.find(text,"m",i)
|
|
||||||
local code = string.sub(text,i,codeEndIdx)
|
|
||||||
--ocelot.log("Code: "..code.." ("..i..", "..codeEndIdx..")")
|
|
||||||
readBreak = readBreak + #code - 1
|
|
||||||
local nums = parseCodeNumbers(code)
|
|
||||||
local codeEnd = code:sub(-1)
|
|
||||||
--ocelot.log("Code end: "..codeEnd..", "..#codeEnd)
|
|
||||||
if codeEnd == "m" then
|
|
||||||
-- Select Graphic Rendition
|
|
||||||
--ocelot.log("Select Graphic Rendition, ID "..nums[1])
|
|
||||||
if nums[1]>=30 and nums[1]<=37 then
|
|
||||||
gpu.setForeground(ANSIColorPalette["dark"][nums[1]%10])
|
|
||||||
end
|
|
||||||
if nums[1]==38 and nums[2]==5 then
|
|
||||||
gpu.setForeground(from8BitColor(nums[3]))
|
|
||||||
end
|
|
||||||
if nums[1]==38 and nums[2]==2 then
|
|
||||||
gpu.setForeground(from24BitColor(nums[3],nums[4],nums[5]))
|
|
||||||
end
|
|
||||||
if nums[1]==39 or nums[1]==0 then
|
|
||||||
gpu.setForeground(defaultForegroundColor)
|
|
||||||
end
|
|
||||||
if nums[1]>=40 and nums[1]<=47 then
|
|
||||||
gpu.setBackground(ANSIColorPalette["dark"][nums[1]%10])
|
|
||||||
end
|
|
||||||
if nums[1]==48 and nums[2]==5 then
|
|
||||||
gpu.setBackground(from8BitColor(nums[3]))
|
|
||||||
end
|
|
||||||
if nums[1]==48 and nums[2]==2 then
|
|
||||||
gpu.setBackground(from24BitColor(nums[3],nums[4],nums[5]))
|
|
||||||
end
|
|
||||||
if nums[1]==49 or nums[1]==0 then
|
|
||||||
gpu.setBackground(defaultBackgroundColor)
|
|
||||||
end
|
|
||||||
if nums[1]>=90 and nums[1]<=97 then
|
|
||||||
gpu.setForeground(ANSIColorPalette["bright"][nums[1]%10])
|
|
||||||
end
|
|
||||||
if nums[1]>=100 and nums[1]<=107 then
|
|
||||||
gpu.setBackground(ANSIColorPalette["bright"][nums[1]%10])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
--gpu.set(termlib.cursorPosX,termlib.cursorPosY,string.sub(text,i,i))
|
|
||||||
section = section..string.sub(text,i,i)
|
|
||||||
end
|
|
||||||
::continue::
|
|
||||||
end
|
|
||||||
printSection()
|
|
||||||
end
|
|
||||||
|
|
||||||
function _G.print(...)
|
|
||||||
local args = {...}
|
|
||||||
local stringArgs = {}
|
|
||||||
for _, arg in pairs(args) do
|
|
||||||
if type(arg)=="table" then
|
|
||||||
table.insert(stringArgs, serialize.table(arg,true))
|
|
||||||
elseif tostring(arg) then
|
|
||||||
table.insert(stringArgs, tostring(arg))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
termlib.write(table.concat(stringArgs, " ") .. "\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
function _G.clear()
|
|
||||||
width, height = gpu.getResolution()
|
|
||||||
gpu.setForeground(defaultForegroundColor)
|
|
||||||
gpu.setBackground(defaultBackgroundColor)
|
|
||||||
gpu.fill(1,1,width,height," ")
|
|
||||||
termlib.cursorPosX, termlib.cursorPosY = 1, 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function _G.read(readHistoryType, prefix, defaultText, maxChars)
|
|
||||||
checkArg(1, readHistoryType, "string", "nil")
|
|
||||||
checkArg(2, prefix, "string", "nil")
|
|
||||||
checkArg(3, defaultText, "string", "nil")
|
|
||||||
checkArg(4, maxChars, "number", "nil")
|
|
||||||
maxChars = maxChars or math.huge
|
|
||||||
|
|
||||||
local text = defaultText or ""
|
|
||||||
|
|
||||||
local historyIdx
|
|
||||||
if readHistoryType then
|
|
||||||
if not termlib.readHistory[readHistoryType] then
|
|
||||||
termlib.readHistory[readHistoryType] = {text}
|
|
||||||
elseif termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType] ] ~= "" then
|
|
||||||
table.insert(termlib.readHistory[readHistoryType], text)
|
|
||||||
end
|
|
||||||
historyIdx = #termlib.readHistory[readHistoryType]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function updateHistory()
|
|
||||||
if not readHistoryType then return end
|
|
||||||
termlib.readHistory[readHistoryType][historyIdx]=text
|
|
||||||
end
|
|
||||||
|
|
||||||
local cur = unicode.len(text)+1
|
|
||||||
if prefix then termlib.write(prefix) end
|
|
||||||
local startX, startY = termlib.cursorPosX, termlib.cursorPosY
|
|
||||||
local fg, bg = gpu.getForeground(), gpu.getBackground()
|
|
||||||
local cursorBlink = true
|
|
||||||
local function get(idx)
|
|
||||||
idx=startX+idx-1
|
|
||||||
return gpu.get(idx%width,startY+(idx//width))
|
|
||||||
end
|
|
||||||
local function checkScroll(y)
|
|
||||||
for i=1,y-height do
|
|
||||||
scrollDown()
|
|
||||||
startY=startY-1
|
|
||||||
end
|
|
||||||
return math.min(y,height)
|
|
||||||
end
|
|
||||||
local function set(idx,chr,rev)
|
|
||||||
if chr==nil or chr=="" then return end
|
|
||||||
if rev then
|
|
||||||
gpu.setForeground(bg)
|
|
||||||
gpu.setBackground(fg)
|
|
||||||
else
|
|
||||||
gpu.setForeground(fg)
|
|
||||||
gpu.setBackground(bg)
|
|
||||||
end
|
|
||||||
idx=startX+idx-1
|
|
||||||
local setX, setY = (idx-1)%width+1, startY+((idx-1)//width+1)-1
|
|
||||||
setY = checkScroll(setY)
|
|
||||||
gpu.set(setX,setY,unicode.sub(chr,1,width-setX+1))
|
|
||||||
for i=1,math.ceil((#chr+setX-1)/width)+1 do
|
|
||||||
gpu.set(1,setY+i,unicode.sub(chr,2-setX+i*width,width+i*width-setX))
|
|
||||||
setY = checkScroll(setY)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local function strDef(a,b)
|
|
||||||
if #a==0 then return b end
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
local function curPos(cur)
|
|
||||||
return unicode.wlen(unicode.sub(text,1,cur-1))+1
|
|
||||||
end
|
|
||||||
local function add(chr)
|
|
||||||
if type(chr)~="string" or #chr==0 then return end
|
|
||||||
if unicode.len(text)>=maxChars then return end
|
|
||||||
if maxChars<math.huge then
|
|
||||||
chr=unicode.sub(chr,1,maxChars-unicode.len(text))
|
|
||||||
end
|
|
||||||
text=unicode.sub(text,1,cur-1)..chr..unicode.sub(text,cur)
|
|
||||||
set(curPos(cur),chr,false)
|
|
||||||
cur=math.min(cur+unicode.len(chr),maxChars+1)
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
|
||||||
cursorBlink = true
|
|
||||||
set(curPos(cur+1),unicode.sub(text,cur+1),false)
|
|
||||||
end
|
|
||||||
local function moveCur(dir)
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
|
||||||
cur=math.max(math.min(cur+dir,unicode.len(text)+1),1)
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
|
||||||
cursorBlink = true
|
|
||||||
end
|
|
||||||
local function isLetter(chr)
|
|
||||||
return not string.find("\x09 :@-./_~?&=%+#",chr,1,true)
|
|
||||||
end
|
|
||||||
local function nextCur(dir,chr,icur)
|
|
||||||
if icur==nil then icur=cur end
|
|
||||||
local next = math.max(math.min(icur+dir,unicode.len(text)+1),1)
|
|
||||||
if chr then return unicode.sub(text,next,next) end
|
|
||||||
return next
|
|
||||||
end
|
|
||||||
local function curAfterWord(dir)
|
|
||||||
local ncur = cur
|
|
||||||
while nextCur(dir,false,ncur)~=ncur and isLetter(nextCur(dir,true,ncur))==(dir==1) do
|
|
||||||
ncur=nextCur(dir,false,ncur)
|
|
||||||
end
|
|
||||||
while nextCur(dir,false,ncur)~=ncur and isLetter(nextCur(dir,true,ncur))==(dir==-1) do
|
|
||||||
ncur=nextCur(dir,false,ncur)
|
|
||||||
end
|
|
||||||
return ncur
|
|
||||||
end
|
|
||||||
local function moveWord(dir)
|
|
||||||
if nextCur(dir)==cur then return end
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
|
||||||
cur=curAfterWord(dir)
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
|
||||||
cursorBlink = true
|
|
||||||
end
|
|
||||||
local function deleteWord(dir)
|
|
||||||
local after = curAfterWord(dir)
|
|
||||||
local lenb = unicode.wlen(text)
|
|
||||||
if dir==1 then
|
|
||||||
text=unicode.sub(text,1,cur-1)..unicode.sub(text,after)
|
|
||||||
set(curPos(cur+1),unicode.sub(text,cur+1)..string.rep(" ",lenb-unicode.wlen(text)+1),false)
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
|
||||||
else
|
|
||||||
text = unicode.sub(text,1,after-1)..unicode.sub(text,cur)
|
|
||||||
cur=after
|
|
||||||
set(curPos(cur+1),unicode.sub(text,cur+1)..string.rep(" ",lenb-unicode.wlen(text)+1),false)
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
|
||||||
end
|
|
||||||
updateHistory()
|
|
||||||
cursorBlink = true
|
|
||||||
end
|
|
||||||
local function isLine(chr)
|
|
||||||
return chr=="\n" or chr=="\r"
|
|
||||||
end
|
|
||||||
--[[ gpu.set(startX,startY,unicode.sub(text,1,width-startX))
|
|
||||||
for i=1,(#text+startX)//width-1 do
|
|
||||||
gpu.set(startX,startY+i,unicode.sub(text,1+i*width,width-startX+i*width))
|
|
||||||
end ]]
|
|
||||||
set(1,text,false)
|
|
||||||
set(curPos(cur)," ",true)
|
|
||||||
|
|
||||||
local function reprint(new)
|
|
||||||
set(1,new..string.rep(" ",unicode.wlen(text)-unicode.wlen(new)+1),false)
|
|
||||||
cur=unicode.len(new)+1
|
|
||||||
text=new
|
|
||||||
set(curPos(cur)," ",true)
|
|
||||||
end
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local args = {event.pull("key_down", "clipboard", 0.5)}
|
|
||||||
if args and args[1] == "key_down" and args[4] then
|
|
||||||
local key = keyboard.keys[args[4]]
|
|
||||||
if key=="up" and readHistoryType then
|
|
||||||
historyIdx=math.max(historyIdx-1,1)
|
|
||||||
reprint(termlib.readHistory[readHistoryType][historyIdx])
|
|
||||||
elseif key=="down" and readHistoryType then
|
|
||||||
historyIdx=math.min(historyIdx+1,#termlib.readHistory[readHistoryType])
|
|
||||||
reprint(termlib.readHistory[readHistoryType][historyIdx])
|
|
||||||
elseif key=="left" and keyboard.ctrlDown then
|
|
||||||
moveWord(-1)
|
|
||||||
elseif key=="right" and keyboard.ctrlDown then
|
|
||||||
moveWord(1)
|
|
||||||
elseif key=="left" then
|
|
||||||
moveCur(-1)
|
|
||||||
elseif key=="right" then
|
|
||||||
moveCur(1)
|
|
||||||
elseif key=="home" then
|
|
||||||
moveCur(-math.huge)
|
|
||||||
elseif key=="end" then
|
|
||||||
moveCur(math.huge)
|
|
||||||
elseif key=="back" and keyboard.ctrlDown then
|
|
||||||
deleteWord(-1)
|
|
||||||
elseif key=="delete" and keyboard.ctrlDown then
|
|
||||||
deleteWord(1)
|
|
||||||
elseif key=="back" and cur>1 then
|
|
||||||
text=unicode.sub(text,1,cur-2)..unicode.sub(text,cur)
|
|
||||||
cur=cur-1
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
|
||||||
cursorBlink = true
|
|
||||||
set(curPos(cur)+1,unicode.sub(text,cur+1).." ",false)
|
|
||||||
updateHistory()
|
|
||||||
elseif key=="delete" then
|
|
||||||
text = unicode.sub(text,1,cur-1)..unicode.sub(text,cur+1)
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
|
|
||||||
cursorBlink = true
|
|
||||||
if cur<=unicode.len(text) then
|
|
||||||
set(curPos(cur+1),unicode.sub(text,cur+1).." ",false)
|
|
||||||
end
|
|
||||||
updateHistory()
|
|
||||||
elseif key=="enter" then
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
|
||||||
break
|
|
||||||
elseif not (args[3]<32 or (args[3]>0x7F and args[3]<=0x9F)) then
|
|
||||||
add(unicode.char(args[3]) or " ")
|
|
||||||
updateHistory()
|
|
||||||
end
|
|
||||||
elseif args and args[1]=="clipboard" then
|
|
||||||
local clip = args[3]
|
|
||||||
if not args[3] then goto continue end
|
|
||||||
while isLine(unicode.sub(clip,1,1)) do clip=unicode.sub(clip,2) end
|
|
||||||
while isLine(unicode.sub(clip,-1)) do clip=unicode.sub(clip,1,-2) end
|
|
||||||
add(clip)
|
|
||||||
updateHistory()
|
|
||||||
else
|
|
||||||
cursorBlink=not cursorBlink
|
|
||||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),cursorBlink)
|
|
||||||
end
|
|
||||||
::continue::
|
|
||||||
end
|
|
||||||
|
|
||||||
if readHistoryType then
|
|
||||||
if termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType]]=="" then
|
|
||||||
table.remove(termlib.readHistory[readHistoryType],#termlib.readHistory[readHistoryType])
|
|
||||||
end
|
|
||||||
if historyIdx<#termlib.readHistory[readHistoryType] then
|
|
||||||
table.remove(termlib.readHistory[readHistoryType],historyIdx)
|
|
||||||
table.insert(termlib.readHistory[readHistoryType],text)
|
|
||||||
end
|
|
||||||
while #termlib.readHistory[readHistoryType] > 50 do
|
|
||||||
table.remove(termlib.readHistory[readHistoryType], 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
termlib.cursorPosX=1
|
|
||||||
termlib.cursorPosY=termlib.cursorPosY+math.ceil((unicode.wlen(text)+startX-1)/width)
|
|
||||||
if termlib.cursorPosY>height then scrollDown() end
|
|
||||||
|
|
||||||
return text
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -1,97 +1 @@
|
|||||||
local compLib
|
Error: no such file. (``)
|
||||||
local LLcomponent
|
|
||||||
if table.copy then
|
|
||||||
compLib = table.copy(component)
|
|
||||||
LLcomponent = table.copy(component)
|
|
||||||
else
|
|
||||||
compLib = {}
|
|
||||||
LLcomponent = component
|
|
||||||
end
|
|
||||||
|
|
||||||
--local ocelot = LLcomponent.proxy(LLcomponent.list("ocelot")())
|
|
||||||
--ocelot.log("loaded")
|
|
||||||
|
|
||||||
_G.componentlib = {["additions"] = {}, ["removals"] = {}}
|
|
||||||
compLib.virtual = {}
|
|
||||||
|
|
||||||
function compLib.virtual.add(address, componentType, proxy)
|
|
||||||
checkArg(1, address, "string")
|
|
||||||
checkArg(2, componentType, "string")
|
|
||||||
checkArg(3, proxy, "table")
|
|
||||||
proxy["address"] = address
|
|
||||||
componentlib.additions[address] = {["componentType"] = componentType, ["proxy"] = proxy}
|
|
||||||
if componentlib.removals[address] then
|
|
||||||
componentlib.removals[address] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function compLib.virtual.remove(address)
|
|
||||||
checkArg(1, address, "string")
|
|
||||||
if componentlib.additions[address] then
|
|
||||||
componentlib.additions[address] = nil
|
|
||||||
else
|
|
||||||
table.insert(componentlib.removals, address)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function compLib.list(componentType)
|
|
||||||
checkArg(1, componentType, "string", "nil")
|
|
||||||
local componentList = table.copy(LLcomponent.list(componentType))
|
|
||||||
for address, dataTable in pairs(componentlib.additions) do
|
|
||||||
if dataTable.componentType == componentType or not componentType then
|
|
||||||
componentList[address] = dataTable.componentType
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, address in pairs(componentlib.removals) do
|
|
||||||
componentList[address] = nil
|
|
||||||
end
|
|
||||||
local i, value
|
|
||||||
setmetatable(componentList, {__call = function(self)
|
|
||||||
i, value = next(self, i)
|
|
||||||
return i, value
|
|
||||||
end})
|
|
||||||
return componentList
|
|
||||||
end
|
|
||||||
|
|
||||||
function compLib.proxy(address)
|
|
||||||
if componentlib.additions[address] then
|
|
||||||
--ocelot.log("vcomponent")
|
|
||||||
return componentlib.additions[address].proxy
|
|
||||||
else
|
|
||||||
return LLcomponent.proxy(address)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function compLib.invoke(address, funcName, ...)
|
|
||||||
--ocelot.log("Invoking " .. funcName .. " from " .. address)
|
|
||||||
if componentlib.additions[address] then
|
|
||||||
--ocelot.log("vcomponent")
|
|
||||||
return componentlib.additions[address].proxy[funcName](...)
|
|
||||||
else
|
|
||||||
return LLcomponent.invoke(address, funcName, ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function compLib.get(address)
|
|
||||||
checkArg(1, address, "string")
|
|
||||||
if #address < 3 then
|
|
||||||
return nil, "abbreviated address must be at least 3 characters long"
|
|
||||||
end
|
|
||||||
for currentAddress, name in compLib.list() do
|
|
||||||
if currentAddress:find("^" .. address) then
|
|
||||||
return(currentAddress)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil, "full address not found"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add main component proxies to the library
|
|
||||||
setmetatable(compLib, {["__index"] = function(_, item)
|
|
||||||
if compLib.list(item)() then
|
|
||||||
return compLib.proxy(compLib.list(item)())
|
|
||||||
else
|
|
||||||
return compLib[item]
|
|
||||||
end
|
|
||||||
end})
|
|
||||||
|
|
||||||
return compLib
|
|
||||||
|
|||||||
+1
-14
@@ -1,14 +1 @@
|
|||||||
local computerlib = table.copy(computer)
|
Error: no such file. (``)
|
||||||
local LLcomputer = table.copy(computer)
|
|
||||||
|
|
||||||
function computerlib.pullSignal(timeout)
|
|
||||||
local startTime = LLcomputer.uptime()
|
|
||||||
local result
|
|
||||||
repeat
|
|
||||||
result = {LLcomputer.pullSignal(0)}
|
|
||||||
coroutine.yield()
|
|
||||||
until result or timeout and LLcomputer.uptime() >= startTime + timeout
|
|
||||||
return table.unpack(result)
|
|
||||||
end
|
|
||||||
|
|
||||||
return computerlib
|
|
||||||
|
|||||||
+1
-55
@@ -1,55 +1 @@
|
|||||||
local computer = import("computer")
|
Error: no such file. (``)
|
||||||
local event = {}
|
|
||||||
|
|
||||||
local bufferTime = 0.1 -- A little bit of buffer time so events won't be skipped by accident.
|
|
||||||
|
|
||||||
--local ocelot = component.proxy(component.list("ocelot")())
|
|
||||||
function event.pull(...)
|
|
||||||
local args = {...}
|
|
||||||
local evtypes, timeout = {}, nil
|
|
||||||
|
|
||||||
for _, arg in pairs(args) do
|
|
||||||
if type(arg) == "number" and not timeout then -- It's a timeout
|
|
||||||
timeout = arg
|
|
||||||
else -- It's an event type
|
|
||||||
table.insert(evtypes, tostring(arg))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local startTime = computer.uptime()
|
|
||||||
|
|
||||||
while true do
|
|
||||||
-- Check event queue for matching event
|
|
||||||
for i = 1, #evmgr.eventQueue do
|
|
||||||
local foundevent = false
|
|
||||||
if evtypes[1] then -- event type(s) specified
|
|
||||||
for _, evtype in pairs(evtypes) do
|
|
||||||
if evmgr.eventQueue[i][2] == evtype and evmgr.eventQueue[i][1] >= startTime - bufferTime then
|
|
||||||
foundevent = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if evmgr.eventQueue[i][1] >= startTime - bufferTime then
|
|
||||||
foundevent = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if foundevent then
|
|
||||||
-- Found matching event (or any event if no type specified)
|
|
||||||
local result = table.copy(evmgr.eventQueue[i])
|
|
||||||
table.remove(evmgr.eventQueue, i)
|
|
||||||
table.remove(result, 1) -- remove the time of event argument
|
|
||||||
return table.unpack(result)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if we've timed out
|
|
||||||
if timeout and computer.uptime() >= startTime + timeout then
|
|
||||||
return nil -- Timed out, return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Yield to allow other processes to run and more events to be added
|
|
||||||
coroutine.yield()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return event
|
|
||||||
|
|||||||
+1
-506
@@ -1,506 +1 @@
|
|||||||
local loadfile = ... -- raw loadfile from boot.lua
|
Error: no such file. (``)
|
||||||
local component, computer
|
|
||||||
|
|
||||||
if loadfile then
|
|
||||||
unicode = loadfile("/halyde/lib/unicode.lua")(loadfile)
|
|
||||||
component = loadfile("/halyde/lib/component.lua")(loadfile)
|
|
||||||
computer = _G.computer
|
|
||||||
elseif import then
|
|
||||||
unicode = import("unicode")
|
|
||||||
component = import("component")
|
|
||||||
computer = import("computer")
|
|
||||||
end
|
|
||||||
|
|
||||||
local filesystem = {}
|
|
||||||
|
|
||||||
function filesystem.canonical(path)
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
local segList = {}
|
|
||||||
if path:sub(1, 1) ~= "/" then
|
|
||||||
path = "/" .. path
|
|
||||||
end
|
|
||||||
path = path:gsub("/+", "/")
|
|
||||||
for segment in path:gmatch("[^/]+") do
|
|
||||||
if segment == ".." and segList[1] then
|
|
||||||
table.remove(segList, #segList)
|
|
||||||
elseif segment ~= "." then
|
|
||||||
table.insert(segList, segment)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return "/" .. table.concat(segList, "/")
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.concat(...)
|
|
||||||
local paths = {...}
|
|
||||||
for i, path in ipairs(paths) do
|
|
||||||
checkArg(i, path, "string")
|
|
||||||
end
|
|
||||||
local currentPath = paths[1]:match("(.+)/?$")
|
|
||||||
for i = 2, #paths do
|
|
||||||
currentPath = currentPath .. "/" .. paths[i]:match("^/?(.+)/?$")
|
|
||||||
end
|
|
||||||
return currentPath
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.absolutePath(path) -- returns the address and absolute path of an object
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
path = filesystem.canonical(path)
|
|
||||||
local address = nil
|
|
||||||
if path:find("^/tmp") then
|
|
||||||
address = computer.tmpAddress()
|
|
||||||
path = path:sub(5)
|
|
||||||
elseif path:find("^/mnt/...") then
|
|
||||||
address = component.get(path:sub(6,8))
|
|
||||||
if not address then
|
|
||||||
address = computer.getBootAddress()
|
|
||||||
else
|
|
||||||
path = path:sub(9)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
address = computer.getBootAddress()
|
|
||||||
end
|
|
||||||
if not address then
|
|
||||||
return nil, "no such component"
|
|
||||||
end
|
|
||||||
return address, path
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.exists(path) -- check if path exists
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
local address, absPath = filesystem.absolutePath(path)
|
|
||||||
if not address then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if absPath:find("^/special/drive/...") then
|
|
||||||
return not not (computer.getBootAddress() and component.get(absPath:sub(16,18)))
|
|
||||||
end
|
|
||||||
if absPath:find("^/special/eeprom/") then
|
|
||||||
return table.find({"init.lua","data.bin","label.txt"},absPath:sub(17))
|
|
||||||
end
|
|
||||||
return component.invoke(address, "exists", absPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function readBytes(self,n)
|
|
||||||
n = n or 1
|
|
||||||
if n==1 then
|
|
||||||
local byte = self:read(1)
|
|
||||||
if byte==nil then return nil end
|
|
||||||
return string.byte(byte)
|
|
||||||
end
|
|
||||||
local bytes, res = {string.byte(self:read(n),1,n)}, 0
|
|
||||||
if self.littleEndian then
|
|
||||||
for i=#bytes,1,-1 do
|
|
||||||
res = (res<<8)&0xFFFFFFFF | bytes[i]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for i=1,#bytes do
|
|
||||||
res = (res<<8)&0xFFFFFFFF | bytes[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
local function readUnicodeChar(self)
|
|
||||||
return unicode.readChar(function()
|
|
||||||
return self:readBytes(1)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function iterateBytes(self)
|
|
||||||
return function()
|
|
||||||
local byte = readBytes(self,1)
|
|
||||||
if byte==nil then self:close() end
|
|
||||||
return byte
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function iterateUnicodeChars(self)
|
|
||||||
return unicode.iterate(iterateBytes(self))
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.makeReadStream(content)
|
|
||||||
local properHandle = {}
|
|
||||||
local readcursor = 1
|
|
||||||
function properHandle.read(self, amount)
|
|
||||||
checkArg(2, amount, "number")
|
|
||||||
local limit = string.len(content)+1
|
|
||||||
local out = nil
|
|
||||||
if readcursor<limit then
|
|
||||||
if amount==math.huge then
|
|
||||||
out = string.sub(content,math.min(readcursor,limit))
|
|
||||||
else
|
|
||||||
out = string.sub(content,math.min(readcursor,limit),math.min(readcursor+amount-1,limit))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
readcursor=readcursor+amount
|
|
||||||
if out=="" then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
properHandle.readBytes = readBytes
|
|
||||||
properHandle.readUnicodeChar = readUnicodeChar
|
|
||||||
properHandle.iterateBytes = iterateBytes
|
|
||||||
properHandle.iterateUnicodeChars = iterateUnicodeChars
|
|
||||||
function properHandle.write()
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
function properHandle.close()
|
|
||||||
content=nil
|
|
||||||
end
|
|
||||||
return properHandle
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.open(path, mode, buffered) -- opens a file and returns its handle
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
checkArg(2, mode, "string", "nil")
|
|
||||||
checkArg(3, buffered, "boolean", "nil")
|
|
||||||
if not mode then
|
|
||||||
mode = "r"
|
|
||||||
end
|
|
||||||
if buffered == nil then
|
|
||||||
buffered = true
|
|
||||||
end
|
|
||||||
if not (mode == "r" or mode == "w" or mode == "rb" or mode == "wb" or mode == "a" or mode == "ab") then
|
|
||||||
return nil, "invalid handle type"
|
|
||||||
end
|
|
||||||
if path:find("^/special") and not filesystem.exists(path) then
|
|
||||||
return nil, "/special does not allow creating files"
|
|
||||||
end
|
|
||||||
local address, absPath = filesystem.absolutePath(path)
|
|
||||||
local unmanagedDrive = address==computer.getBootAddress() and absPath:find("^/special/drive")
|
|
||||||
local unmanagedProxy, sectorSize, sectorCount, handle
|
|
||||||
if unmanagedDrive then
|
|
||||||
unmanagedProxy = component.proxy(component.get(absPath:sub(16,18)))
|
|
||||||
sectorSize = unmanagedProxy.getSectorSize()
|
|
||||||
sectorCount = math.ceil(unmanagedProxy.getCapacity()/sectorSize)
|
|
||||||
elseif not (address==computer.getBootAddress() and absPath:find("^/special/")) then
|
|
||||||
local handleArgs = {component.invoke(address, "open", absPath, mode)}
|
|
||||||
handle = handleArgs[1]
|
|
||||||
if not handle then
|
|
||||||
return table.unpack(handleArgs)
|
|
||||||
end
|
|
||||||
handleArgs = nil
|
|
||||||
end
|
|
||||||
local properHandle = {}
|
|
||||||
properHandle.handle = handle
|
|
||||||
properHandle.address = address
|
|
||||||
local content = nil
|
|
||||||
local readcursor = 1
|
|
||||||
if buffered and mode:sub(1,1)=="r" then
|
|
||||||
content=""
|
|
||||||
repeat
|
|
||||||
tmpdata = component.invoke(address, "read", handle, math.huge or math.maxinteger)
|
|
||||||
content = content .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
component.invoke(address, "close", handle)
|
|
||||||
end
|
|
||||||
function properHandle.read(self, amount)
|
|
||||||
checkArg(2, amount, "number")
|
|
||||||
if unmanagedDrive then
|
|
||||||
local sectorIdx = ((readcursor-1)//sectorSize)+1
|
|
||||||
if sectorIdx>sectorCount then return nil end
|
|
||||||
local sector = unmanagedProxy.readSector(sectorIdx)
|
|
||||||
local data = sector:sub(((readcursor-1)%sectorSize)+1,((readcursor+math.min(amount,sectorSize)-2)%sectorSize)+1)
|
|
||||||
readcursor=readcursor+#data
|
|
||||||
if data=="" then return nil end
|
|
||||||
return data
|
|
||||||
else
|
|
||||||
if buffered then
|
|
||||||
local limit = string.len(content)+1
|
|
||||||
local out = nil
|
|
||||||
if readcursor<limit then
|
|
||||||
if amount==math.huge then
|
|
||||||
out = string.sub(content,math.min(readcursor,limit))
|
|
||||||
else
|
|
||||||
out = string.sub(content,math.min(readcursor,limit),math.min(readcursor+amount-1,limit))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
readcursor=readcursor+amount
|
|
||||||
if out=="" then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
else
|
|
||||||
return component.invoke(self.address, "read", self.handle, amount)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
properHandle.readBytes = readBytes
|
|
||||||
properHandle.readUnicodeChar = readUnicodeChar
|
|
||||||
properHandle.iterateBytes = iterateBytes
|
|
||||||
properHandle.iterateUnicodeChars = iterateUnicodeChars
|
|
||||||
function properHandle.write(self, data)
|
|
||||||
checkArg(2, data, "string")
|
|
||||||
if unmanagedDrive then
|
|
||||||
local startSector = ((readcursor-1)//sectorSize)+1
|
|
||||||
if startSector>sectorCount then return nil, "not enough space" end
|
|
||||||
local startSByte = ((readcursor-1)%sectorSize)+1
|
|
||||||
local sect = unmanagedProxy.readSector(startSector)
|
|
||||||
unmanagedProxy.writeSector(startSector,sect:sub(1,startSByte-1)..data:sub(1,sectorSize-startSByte+1))
|
|
||||||
for i=2,(#data+startSByte)//sectorSize do
|
|
||||||
if startSector+i-1>sectorCount then return nil, "not enough space" end
|
|
||||||
unmanagedProxy.writeSector(startSector+i-1,data:sub(startSByte+sectorSize*(i-1),startSByte+sectorSize*i-1))
|
|
||||||
end
|
|
||||||
readcursor=readcursor+#data
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return component.invoke(self.address, "write", self.handle, data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function properHandle.close(self)
|
|
||||||
if buffered then
|
|
||||||
content = nil
|
|
||||||
else
|
|
||||||
return component.invoke(self.address, "close", self.handle)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if address==computer.getBootAddress() then
|
|
||||||
local eeprom
|
|
||||||
pcall(function()
|
|
||||||
eeprom = component.eeprom
|
|
||||||
end)
|
|
||||||
if eeprom then
|
|
||||||
local getFunc, setFunc
|
|
||||||
if absPath=="/special/eeprom/init.lua" then
|
|
||||||
getFunc,setFunc = "get","set"
|
|
||||||
elseif absPath=="/special/eeprom/data.bin" then
|
|
||||||
getFunc,setFunc = "getData","setData"
|
|
||||||
elseif absPath=="/special/eeprom/label.txt" then
|
|
||||||
getFunc,setFunc = "getLabel","setLabel"
|
|
||||||
end
|
|
||||||
if mode:sub(1,1)=="r" and getFunc then
|
|
||||||
local stream = filesystem.makeReadStream(eeprom[getFunc]() or "")
|
|
||||||
properHandle.read = stream.read
|
|
||||||
properHandle.close = stream.close
|
|
||||||
elseif mode:sub(1,1)=="w" and setFunc then
|
|
||||||
local content = ""
|
|
||||||
function properHandle.write(self, data)
|
|
||||||
checkArg(2, data, "string")
|
|
||||||
content=content..data
|
|
||||||
end
|
|
||||||
function properHandle.close(self)
|
|
||||||
return eeprom[setFunc](content)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return properHandle
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.list(path)
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
path = filesystem.canonical(path)
|
|
||||||
if path == "/mnt" then
|
|
||||||
-- list drives
|
|
||||||
local returnTable = {}
|
|
||||||
local tmpAddress = computer.tmpAddress()
|
|
||||||
for address, _ in component.list("filesystem") do
|
|
||||||
if address~=tmpAddress then
|
|
||||||
table.insert(returnTable, address:sub(1, 3) .. "/")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return returnTable
|
|
||||||
elseif path == "/special/drive" then
|
|
||||||
local returnTable = {}
|
|
||||||
local tmpAddress = computer.tmpAddress()
|
|
||||||
for address, type in component.list("drive") do
|
|
||||||
if address~=tmpAddress and type=="drive" then
|
|
||||||
table.insert(returnTable, address:sub(1, 3))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return returnTable
|
|
||||||
elseif path=="/special/eeprom" then
|
|
||||||
return {"init.lua","data.bin","label.txt"}
|
|
||||||
else
|
|
||||||
local address, absPath = filesystem.absolutePath(path)
|
|
||||||
if not address then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return component.invoke(address, "list", absPath)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.size(path)
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
local address, absPath = filesystem.absolutePath(path)
|
|
||||||
if not address then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if address==computer.getBootAddress() then
|
|
||||||
if absPath:find("^/special/drive") then
|
|
||||||
local drive = component.get(absPath:sub(16,18))
|
|
||||||
if not drive then return false end
|
|
||||||
return component.invoke(drive,"getCapacity")
|
|
||||||
elseif absPath:find("^/special/eeprom") then
|
|
||||||
local eeprom
|
|
||||||
pcall(function()
|
|
||||||
eeprom = component.eeprom
|
|
||||||
end)
|
|
||||||
if eeprom then
|
|
||||||
local getFunc
|
|
||||||
if absPath=="/special/eeprom/init.lua" then
|
|
||||||
getFunc = "get"
|
|
||||||
elseif absPath=="/special/eeprom/data.bin" then
|
|
||||||
getFunc = "getData"
|
|
||||||
elseif absPath=="/special/eeprom/label.txt" then
|
|
||||||
getFunc = "getLabel"
|
|
||||||
end
|
|
||||||
return #(eeprom[getFunc]())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return component.invoke(address, "size", absPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getRecursiveList(address,absPath)
|
|
||||||
local list = component.invoke(address,"list",absPath)
|
|
||||||
local dirList = {}
|
|
||||||
local listChanged = true
|
|
||||||
while listChanged do
|
|
||||||
listChanged = false
|
|
||||||
for i=1,#list do
|
|
||||||
if component.invoke(address, "isDirectory", absPath.."/"..list[i]) then
|
|
||||||
listChanged = true
|
|
||||||
local dir = list[i]
|
|
||||||
if dir:sub(-1)=="/" then dir=dir:sub(1,-2) end
|
|
||||||
table.insert(dirList,dir)
|
|
||||||
table.remove(list,i)
|
|
||||||
local subDir = component.invoke(address,"list",absPath.."/"..dir)
|
|
||||||
for j=1,#subDir do table.insert(list,dir.."/"..subDir[j]) end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return list,dirList
|
|
||||||
end
|
|
||||||
|
|
||||||
local function copyContent(fromHandle,toHandle)
|
|
||||||
if not (fromHandle and toHandle) then return end
|
|
||||||
local memory = math.floor(computer.freeMemory()*0.8)
|
|
||||||
local tmpdata
|
|
||||||
while true do
|
|
||||||
tmpdata = fromHandle:read(memory)
|
|
||||||
if not tmpdata then break end
|
|
||||||
local status,reason = toHandle:write(tmpdata)
|
|
||||||
if status~=true then break end
|
|
||||||
end
|
|
||||||
fromHandle:close()
|
|
||||||
toHandle:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
|
|
||||||
-- TODO: make this use copyContent
|
|
||||||
if fromAbsPath:sub(-1)=="/" then fromAbsPath=fromAbsPath:sub(1,-2) end
|
|
||||||
if toAbsPath:sub(-1)=="/" then toAbsPath=toAbsPath:sub(1,-2) end
|
|
||||||
component.invoke(toAddress,"makeDirectory",toAbsPath)
|
|
||||||
local fileList,dirList = getRecursiveList(fromAddress,fromAbsPath)
|
|
||||||
for i=1,#dirList do
|
|
||||||
component.invoke(toAddress,"makeDirectory",toAbsPath.."/"..dirList[i])
|
|
||||||
end
|
|
||||||
for i=1,#fileList do
|
|
||||||
local fromFile, toFile = fromAbsPath.."/"..fileList[i], toAbsPath.."/"..fileList[i]
|
|
||||||
--[[ local handle = component.invoke(fromAddress, "open", fromFile, "r")
|
|
||||||
local data, tmpdata = "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = component.invoke(fromAddress, "read", handle, math.huge or math.maxinteger)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
tmpdata = component.invoke(fromAddress, "close", handle)
|
|
||||||
local handle = component.invoke(toAddress, "open", toFile, "w")
|
|
||||||
component.invoke(toAddress, "write", handle, data)
|
|
||||||
component.invoke(toAddress, "close", handle) ]]
|
|
||||||
local fromHandle = component.invoke(fromAddress, "open", fromFile, "r")
|
|
||||||
local toHandle = component.invoke(toAddress, "open", toFile, "w")
|
|
||||||
copyContent({
|
|
||||||
["read"]=function(...) return component.invoke(fromAddress, "read", handle, ...) end,
|
|
||||||
["close"]=function(...) return component.invoke(fromAddress, "close", handle, ...) end
|
|
||||||
},{
|
|
||||||
["write"]=function(...) return component.invoke(fromAddress, "write", handle, ...) end,
|
|
||||||
["close"]=function(...) return component.invoke(fromAddress, "close", handle, ...) end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.isDirectory(path)
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
local address, absPath = filesystem.absolutePath(path)
|
|
||||||
if not address then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return component.invoke(address, "isDirectory", absPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.rename(fromPath, toPath)
|
|
||||||
checkArg(1, fromPath, "string")
|
|
||||||
checkArg(2, toPath, "string")
|
|
||||||
local fromAddress, fromAbsPath = filesystem.absolutePath(fromPath)
|
|
||||||
local toAddress, toAbsPath = filesystem.absolutePath(toPath)
|
|
||||||
if not fromAddress or not toAddress then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if fromAddress == toAddress then
|
|
||||||
return component.invoke(fromAddress, "rename", fromAbsPath, toAbsPath)
|
|
||||||
elseif filesystem.isDirectory(fromPath) then -- component.invoke(fromAddress, "isDirectory", fromAbsPath) then
|
|
||||||
copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
|
|
||||||
filesystem.remove(fromPath) -- component.invoke(fromAddress,"remove", fromAbsPath)
|
|
||||||
else
|
|
||||||
local handle, data, tmpdata = filesystem.open(fromPath), "", nil -- component.invoke(fromAddress, "open", fromAbsPath, "r"), "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge or math.maxinteger) -- component.invoke(fromAddress, "read", handle, math.huge or math.maxinteger)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
tmpdata = handle:close() -- component.invoke(fromAddress, "close", handle)
|
|
||||||
local handle = filesystem.open(toPath) -- component.invoke(toAddress, "open", toAbsPath, "w")
|
|
||||||
handle:write(data) -- component.invoke(toAddress, "write", handle, data)
|
|
||||||
handle:close() -- component.invoke(toAddress, "close", handle)
|
|
||||||
filesystem.remove(fromPath) -- component.invoke(fromAddress, "remove", fromAbsPath)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.copy(fromPath, toPath)
|
|
||||||
checkArg(1, fromPath, "string")
|
|
||||||
checkArg(2, toPath, "string")
|
|
||||||
local fromAddress, fromAbsPath = filesystem.absolutePath(fromPath)
|
|
||||||
local toAddress, toAbsPath = filesystem.absolutePath(toPath)
|
|
||||||
if not fromAddress or not toAddress then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if filesystem.isDirectory(fromPath) then -- component.invoke(fromAddress, "isDirectory", fromAbsPath)
|
|
||||||
copyRecursive(fromAddress,fromAbsPath,toAddress,toAbsPath)
|
|
||||||
else
|
|
||||||
--[[ local handle = filesystem.open(fromPath,"r")
|
|
||||||
local data, tmpdata = "", nil
|
|
||||||
repeat
|
|
||||||
tmpdata = handle:read(math.huge or math.maxinteger)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
tmpdata = handle:close()
|
|
||||||
local handle = filesystem.open(toPath,"w")
|
|
||||||
handle:write(data)
|
|
||||||
handle:close() ]]
|
|
||||||
copyContent(filesystem.open(fromPath,"r"),filesystem.open(toPath,"w"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.remove(path)
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
local address, absPath = filesystem.absolutePath(path)
|
|
||||||
if not address then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if absPath:find("^/special") then return false end
|
|
||||||
if absPath:find("^/tmp") then return false end
|
|
||||||
if absPath:find("^/mnt") then return false end
|
|
||||||
return component.invoke(address, "remove", absPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function filesystem.makeDirectory(path)
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
local address, absPath = filesystem.absolutePath(path)
|
|
||||||
if not address then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return component.invoke(address, "makeDirectory", absPath)
|
|
||||||
end
|
|
||||||
|
|
||||||
return(filesystem)
|
|
||||||
|
|||||||
+1
-4
@@ -1,4 +1 @@
|
|||||||
-- json.lua by rxi
|
Error: no such file. (``)
|
||||||
-- Minified with luamin
|
|
||||||
-- Original: https://github.com/rxi/json.lua
|
|
||||||
local a={_version="0.1.2"}local b;local c={["\\"]="\\",["\""]="\"",["\b"]="b",["\f"]="f",["\n"]="n",["\r"]="r",["\t"]="t"}local d={["/"]="/"}for e,f in pairs(c)do d[f]=e end;local function g(h)return"\\"..(c[h]or string.format("u%04x",h:byte()))end;local function i(j)return"null"end;local function k(j,l)local m={}l=l or{}if l[j]then error("circular reference")end;l[j]=true;if rawget(j,1)~=nil or next(j)==nil then local n=0;for e in pairs(j)do if type(e)~="number"then error("invalid table: mixed or invalid key types")end;n=n+1 end;if n~=#j then error("invalid table: sparse array")end;for o,f in ipairs(j)do table.insert(m,b(f,l))end;l[j]=nil;return"["..table.concat(m,",").."]"else for e,f in pairs(j)do if type(e)~="string"then error("invalid table: mixed or invalid key types")end;table.insert(m,b(e,l)..":"..b(f,l))end;l[j]=nil;return"{"..table.concat(m,",").."}"end end;local function p(j)return'"'..j:gsub('[%z\1-\31\\"]',g)..'"'end;local function q(j)if j~=j or j<=-math.huge or j>=math.huge then error("unexpected number value '"..tostring(j).."'")end;return string.format("%.14g",j)end;local r={["nil"]=i,["table"]=k,["string"]=p,["number"]=q,["boolean"]=tostring}b=function(j,l)local s=type(j)local t=r[s]if t then return t(j,l)end;error("unexpected type '"..s.."'")end;function a.encode(j)return b(j)end;local u;local function v(...)local m={}for o=1,select("#",...)do m[select(o,...)]=true end;return m end;local w=v(" ","\t","\r","\n")local x=v(" ","\t","\r","\n","]","}",",")local y=v("\\","/",'"',"b","f","n","r","t","u")local z=v("true","false","null")local A={["true"]=true,["false"]=false,["null"]=nil}local function B(C,D,E,F)for o=D,#C do if E[C:sub(o,o)]~=F then return o end end;return#C+1 end;local function G(C,D,H)local I=1;local J=1;for o=1,D-1 do J=J+1;if C:sub(o,o)=="\n"then I=I+1;J=1 end end;error(string.format("%s at line %d col %d",H,I,J))end;local function K(n)local t=math.floor;if n<=0x7f then return string.char(n)elseif n<=0x7ff then return string.char(t(n/64)+192,n%64+128)elseif n<=0xffff then return string.char(t(n/4096)+224,t(n%4096/64)+128,n%64+128)elseif n<=0x10ffff then return string.char(t(n/262144)+240,t(n%262144/4096)+128,t(n%4096/64)+128,n%64+128)end;error(string.format("invalid unicode codepoint '%x'",n))end;local function L(M)local N=tonumber(M:sub(1,4),16)local O=tonumber(M:sub(7,10),16)if O then return K((N-0xd800)*0x400+O-0xdc00+0x10000)else return K(N)end end;local function P(C,o)local m=""local Q=o+1;local e=Q;while Q<=#C do local R=C:byte(Q)if R<32 then G(C,Q,"control character in string")elseif R==92 then m=m..C:sub(e,Q-1)Q=Q+1;local h=C:sub(Q,Q)if h=="u"then local S=C:match("^[dD][89aAbB]%x%x\\u%x%x%x%x",Q+1)or C:match("^%x%x%x%x",Q+1)or G(C,Q-1,"invalid unicode escape in string")m=m..L(S)Q=Q+#S else if not y[h]then G(C,Q-1,"invalid escape char '"..h.."' in string")end;m=m..d[h]end;e=Q+1 elseif R==34 then m=m..C:sub(e,Q-1)return m,Q+1 end;Q=Q+1 end;G(C,o,"expected closing quote for string")end;local function T(C,o)local R=B(C,o,x)local M=C:sub(o,R-1)local n=tonumber(M)if not n then G(C,o,"invalid number '"..M.."'")end;return n,R end;local function U(C,o)local R=B(C,o,x)local V=C:sub(o,R-1)if not z[V]then G(C,o,"invalid literal '"..V.."'")end;return A[V],R end;local function W(C,o)local m={}local n=1;o=o+1;while 1 do local R;o=B(C,o,w,true)if C:sub(o,o)=="]"then o=o+1;break end;R,o=u(C,o)m[n]=R;n=n+1;o=B(C,o,w,true)local X=C:sub(o,o)o=o+1;if X=="]"then break end;if X~=","then G(C,o,"expected ']' or ','")end end;return m,o end;local function Y(C,o)local m={}o=o+1;while 1 do local Z,j;o=B(C,o,w,true)if C:sub(o,o)=="}"then o=o+1;break end;if C:sub(o,o)~='"'then G(C,o,"expected string for key")end;Z,o=u(C,o)o=B(C,o,w,true)if C:sub(o,o)~=":"then G(C,o,"expected ':' after key")end;o=B(C,o+1,w,true)j,o=u(C,o)m[Z]=j;o=B(C,o,w,true)local X=C:sub(o,o)o=o+1;if X=="}"then break end;if X~=","then G(C,o,"expected '}' or ','")end end;return m,o end;local _={['"']=P,["0"]=T,["1"]=T,["2"]=T,["3"]=T,["4"]=T,["5"]=T,["6"]=T,["7"]=T,["8"]=T,["9"]=T,["-"]=T,["t"]=U,["f"]=U,["n"]=U,["["]=W,["{"]=Y}u=function(C,D)local X=C:sub(D,D)local t=_[X]if t then return t(C,D)end;G(C,D,"unexpected character '"..X.."'")end;function a.decode(C)if type(C)~="string"then error("expected argument of type string, got "..type(C))end;local m,D=u(C,B(C,1,w,true))D=B(C,D,w,true)if D<=#C then G(C,D,"trailing garbage")end;return m end;return a
|
|
||||||
|
|||||||
+1
-27
File diff suppressed because one or more lines are too long
+1
-432
@@ -1,432 +1 @@
|
|||||||
local raster = {
|
Error: no such file. (``)
|
||||||
["units"]={},
|
|
||||||
["defaultBackgroundColor"]=0x000000,
|
|
||||||
["defaultForegroundColor"]=0xFFFFFF,
|
|
||||||
["displayWidth"]=0,
|
|
||||||
["displayHeight"]=0,
|
|
||||||
["charWidth"]=0,
|
|
||||||
["charHeight"]=0,
|
|
||||||
["backgroundColor"]=0xFFFFFF
|
|
||||||
}
|
|
||||||
|
|
||||||
local component = import("component")
|
|
||||||
-- local ocelot = component.proxy(component.list("ocelot")())
|
|
||||||
local gpu = component.gpu
|
|
||||||
|
|
||||||
local display = {}
|
|
||||||
local chunksAffected = {}
|
|
||||||
|
|
||||||
local renderBuffer = nil
|
|
||||||
|
|
||||||
-- braille rendering
|
|
||||||
|
|
||||||
function raster.units.charToBraille(x,y)
|
|
||||||
return x*2,y*4
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.units.brailleToChar(x,y)
|
|
||||||
return math.ceil(x/2),math.ceil(y/4)
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.init(width, height, bgcolor)
|
|
||||||
-- NOTE: Width and height are in characters, not pixels in braille.
|
|
||||||
-- If the width and height are nil, the entire screen will be used.
|
|
||||||
if width==nil and height==nil then
|
|
||||||
width, height = gpu.getResolution()
|
|
||||||
end
|
|
||||||
|
|
||||||
raster.charWidth = width
|
|
||||||
raster.charHeight = height
|
|
||||||
|
|
||||||
width, height = raster.units.charToBraille(width, height)
|
|
||||||
|
|
||||||
bgcolor = bgcolor or raster.defaultBackgroundColor
|
|
||||||
if bgcolor~=0 then
|
|
||||||
for i=1,width*height do
|
|
||||||
display[i]=bgcolor
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
raster.displayWidth = width
|
|
||||||
raster.displayHeight = height
|
|
||||||
raster.backgroundColor = bgcolor
|
|
||||||
|
|
||||||
pcall(function()
|
|
||||||
renderBuffer = gpu.allocateBuffer()
|
|
||||||
end)
|
|
||||||
|
|
||||||
raster.clear()
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.set(x, y, color)
|
|
||||||
if x<1 or x>raster.displayWidth or y<1 or y>raster.displayHeight then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
color = color or raster.defaultForegroundColor
|
|
||||||
local i = x+y*raster.displayWidth
|
|
||||||
display[i] = color
|
|
||||||
|
|
||||||
local ci = math.floor((x-1)/2)+math.floor((y-1)/4)*raster.charWidth+1
|
|
||||||
-- ocelot.log(x..","..y..":"..ci)
|
|
||||||
chunksAffected[ci] = true
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.get(x, y)
|
|
||||||
local i = x+y*raster.displayWidth
|
|
||||||
return display[i] or raster.backgroundColor
|
|
||||||
end
|
|
||||||
|
|
||||||
local function stats(arr)
|
|
||||||
local out = {}
|
|
||||||
for i=1,#arr do
|
|
||||||
local v = arr[i]
|
|
||||||
if out[v]==nil then
|
|
||||||
out[v]=1
|
|
||||||
else
|
|
||||||
out[v] = out[v] + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getKeys(t)
|
|
||||||
local keys = {}
|
|
||||||
for k,v in pairs(t) do
|
|
||||||
table.insert(keys,{k,v})
|
|
||||||
end
|
|
||||||
table.sort(keys,function(a,b)
|
|
||||||
return a[2]>b[2]
|
|
||||||
end)
|
|
||||||
for i=1,#keys do
|
|
||||||
keys[i] = keys[i][1]
|
|
||||||
end
|
|
||||||
return keys
|
|
||||||
end
|
|
||||||
|
|
||||||
local function colorDifference(a,b)
|
|
||||||
return ((a>>16)&255)-((b>>16)&255)+((a>>8)&255)-((b>>8)&255)+(a&255)-(b&255)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function limitTwoColors(arr)
|
|
||||||
local colors = getKeys(stats(arr))
|
|
||||||
for i=1,#arr do
|
|
||||||
local v=arr[i]
|
|
||||||
if v==colors[1] then
|
|
||||||
arr[i]=0
|
|
||||||
goto continue
|
|
||||||
elseif v==colors[2] then
|
|
||||||
arr[i]=1
|
|
||||||
goto continue
|
|
||||||
else
|
|
||||||
--error("Pixel is not in the two colors (raster.lua:90)")
|
|
||||||
-- get closest color so atleast it kinda shows
|
|
||||||
if colorDifference(v,colors[1])<colorDifference(v,colors[2]) then
|
|
||||||
arr[i]=0
|
|
||||||
else
|
|
||||||
arr[i]=1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
::continue::
|
|
||||||
end
|
|
||||||
return arr,colors[1] or 0,colors[2] or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local function arrayToBraille(arr)
|
|
||||||
local codePoint = 0x2800
|
|
||||||
for i=1,8 do
|
|
||||||
codePoint = codePoint | arr[i]<<(i-1)
|
|
||||||
end
|
|
||||||
if codePoint==0x2800 then return " " end
|
|
||||||
return utf8.char(codePoint)
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.update()
|
|
||||||
if renderBuffer~=nil then
|
|
||||||
gpu.setActiveBuffer(renderBuffer)
|
|
||||||
end
|
|
||||||
for y=1,raster.displayHeight,4 do
|
|
||||||
-- gpu.set(0,0,tostring(y))
|
|
||||||
for x=1,raster.displayWidth,2 do
|
|
||||||
local ci = math.floor(x/2)+math.floor(y/4)*raster.charWidth+1
|
|
||||||
if chunksAffected[ci] then
|
|
||||||
local chunk = {
|
|
||||||
raster.get(x,y),
|
|
||||||
raster.get(x,y+1),
|
|
||||||
raster.get(x,y+2),
|
|
||||||
raster.get(x+1,y),
|
|
||||||
raster.get(x+1,y+1),
|
|
||||||
raster.get(x+1,y+2),
|
|
||||||
raster.get(x,y+3),
|
|
||||||
raster.get(x+1,y+3)
|
|
||||||
}
|
|
||||||
local colorA = nil
|
|
||||||
local colorB = nil
|
|
||||||
chunk,colorA,colorB = limitTwoColors(chunk)
|
|
||||||
-- print(tostring(colorA)..","..tostring(colorB))
|
|
||||||
cx,cy=raster.units.brailleToChar(x,y)
|
|
||||||
gpu.setBackground(colorA)
|
|
||||||
gpu.setForeground(colorB)
|
|
||||||
-- gpu.set(cx,cy,tostring(colorB/0xFFFFFF))
|
|
||||||
gpu.set(cx,cy,arrayToBraille(chunk))
|
|
||||||
chunksAffected[ci] = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if renderBuffer~=nil then
|
|
||||||
gpu.bitblt()
|
|
||||||
gpu.setActiveBuffer(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.clear()
|
|
||||||
if renderBuffer~=nil then
|
|
||||||
gpu.setActiveBuffer(renderBuffer)
|
|
||||||
end
|
|
||||||
-- clear()
|
|
||||||
local bgcolor = raster.backgroundColor
|
|
||||||
gpu.setBackground(bgcolor)
|
|
||||||
gpu.fill(1,1,raster.displayWidth,raster.displayHeight," ")
|
|
||||||
display = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.free()
|
|
||||||
if renderBuffer==nil then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return gpu.freeBuffer(renderBuffer)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- advanced rendering
|
|
||||||
|
|
||||||
function raster.drawLine(x1, y1, x2, y2, color)
|
|
||||||
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
|
|
||||||
|
|
||||||
local dx = math.abs(x2 - x1)
|
|
||||||
local dy = math.abs(y2 - y1)
|
|
||||||
|
|
||||||
local sx = x1 < x2 and 1 or -1
|
|
||||||
local sy = y1 < y2 and 1 or -1
|
|
||||||
|
|
||||||
local err = dx - dy
|
|
||||||
|
|
||||||
while true do
|
|
||||||
raster.set(x1, y1, color)
|
|
||||||
|
|
||||||
if x1 == x2 and y1 == y2 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local e2 = 2 * err
|
|
||||||
|
|
||||||
if e2 > -dy then
|
|
||||||
err = err - dy
|
|
||||||
x1 = x1 + sx
|
|
||||||
end
|
|
||||||
|
|
||||||
if e2 < dx then
|
|
||||||
err = err + dx
|
|
||||||
y1 = y1 + sy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.drawRect(x1,y1,x2,y2,col)
|
|
||||||
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
|
|
||||||
if x1 > x2 then x1, x2 = x2, x1 end
|
|
||||||
if y1 > y2 then y1, y2 = y2, y1 end
|
|
||||||
for x=x1,x2 do
|
|
||||||
raster.set(x,y1,col)
|
|
||||||
raster.set(x,y2,col)
|
|
||||||
end
|
|
||||||
for y=y1+1,y2-1 do
|
|
||||||
raster.set(x1,y,col)
|
|
||||||
raster.set(x2,y,col)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.fillRect(x1,y1,x2,y2,col)
|
|
||||||
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
|
|
||||||
if x1 > x2 then x1, x2 = x2, x1 end
|
|
||||||
if y1 > y2 then y1, y2 = y2, y1 end
|
|
||||||
for x=x1,x2 do
|
|
||||||
for y=y1,y2 do
|
|
||||||
raster.set(x,y,col)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.drawCircle(xc, yc, radius, color)
|
|
||||||
xc=math.floor(xc)
|
|
||||||
yc=math.floor(yc)
|
|
||||||
radius=math.floor(radius)
|
|
||||||
local x = 0
|
|
||||||
local y = radius
|
|
||||||
local d = 3 - 2 * radius
|
|
||||||
|
|
||||||
while y >= x do
|
|
||||||
-- Draw 8 symmetric points
|
|
||||||
raster.set(xc + x, yc + y, color)
|
|
||||||
raster.set(xc - x, yc + y, color)
|
|
||||||
raster.set(xc + x, yc - y, color)
|
|
||||||
raster.set(xc - x, yc - y, color)
|
|
||||||
raster.set(xc + y, yc + x, color)
|
|
||||||
raster.set(xc - y, yc + x, color)
|
|
||||||
raster.set(xc + y, yc - x, color)
|
|
||||||
raster.set(xc - y, yc - x, color)
|
|
||||||
|
|
||||||
if d < 0 then
|
|
||||||
d = d + 4 * x + 6
|
|
||||||
else
|
|
||||||
d = d + 4 * (x - y) + 10
|
|
||||||
y = y - 1
|
|
||||||
end
|
|
||||||
x = x + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.drawEllipse(x1, y1, x2, y2, color)
|
|
||||||
if x1 > x2 then x1, x2 = x2, x1 end
|
|
||||||
if y1 > y2 then y1, y2 = y2, y1 end
|
|
||||||
|
|
||||||
local xc = math.floor((x1 + x2) / 2)
|
|
||||||
local yc = math.floor((y1 + y2) / 2)
|
|
||||||
|
|
||||||
local a = math.floor((x2 - x1) / 2)
|
|
||||||
local b = math.floor((y2 - y1) / 2)
|
|
||||||
|
|
||||||
if a <= 0 or b <= 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if a == b then
|
|
||||||
raster.drawCircle(xc, yc, a, color)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if a <= 1 and b <= 1 then
|
|
||||||
raster.set(xc, yc, color)
|
|
||||||
return
|
|
||||||
elseif a <= 1 then
|
|
||||||
for y = yc - b, yc + b do
|
|
||||||
raster.set(xc, y, color)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
elseif b <= 1 then
|
|
||||||
for x = xc - a, xc + a do
|
|
||||||
raster.set(x, yc, color)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local x = 0
|
|
||||||
local y = b
|
|
||||||
local a2 = a * a
|
|
||||||
local b2 = b * b
|
|
||||||
|
|
||||||
local d1 = b2 - (a2 * b) + (0.25 * a2)
|
|
||||||
local dx = 2 * b2 * x
|
|
||||||
local dy = 2 * a2 * y
|
|
||||||
|
|
||||||
while dx < dy do
|
|
||||||
raster.set(xc + x, yc + y, color)
|
|
||||||
raster.set(xc - x, yc + y, color)
|
|
||||||
raster.set(xc + x, yc - y, color)
|
|
||||||
|
|
||||||
if d1 < 0 then
|
|
||||||
x = x + 1
|
|
||||||
dx = dx + (2 * b2)
|
|
||||||
d1 = d1 + dx + b2
|
|
||||||
else
|
|
||||||
x = x + 1
|
|
||||||
y = y - 1
|
|
||||||
dx = dx + (2 * b2)
|
|
||||||
dy = dy - (2 * a2)
|
|
||||||
d1 = d1 + dx - dy + b2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local d2 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2
|
|
||||||
|
|
||||||
while y >= 0 do
|
|
||||||
raster.set(xc + x, yc + y, color)
|
|
||||||
raster.set(xc - x, yc + y, color)
|
|
||||||
raster.set(xc + x, yc - y, color)
|
|
||||||
raster.set(xc - x, yc - y, color)
|
|
||||||
|
|
||||||
if d2 > 0 then
|
|
||||||
y = y - 1
|
|
||||||
dy = dy - (2 * a2)
|
|
||||||
d2 = d2 - dy + a2
|
|
||||||
else
|
|
||||||
y = y - 1
|
|
||||||
x = x + 1
|
|
||||||
dx = dx + (2 * b2)
|
|
||||||
dy = dy - (2 * a2)
|
|
||||||
d2 = d2 + dx - dy + a2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.fillCircle(x, y, r, color)
|
|
||||||
x, y = math.floor(x + 0.5), math.floor(y + 0.5)
|
|
||||||
r = math.floor(r + 0.5)
|
|
||||||
|
|
||||||
if r <= 0 then return end
|
|
||||||
|
|
||||||
local minX, maxX = x - r, x + r
|
|
||||||
local minY, maxY = y - r, y + r
|
|
||||||
|
|
||||||
for py = minY, maxY do
|
|
||||||
for px = minX, maxX do
|
|
||||||
local dx, dy = px - x, py - y
|
|
||||||
local distSquared = dx*dx + dy*dy
|
|
||||||
|
|
||||||
if distSquared <= r*r then
|
|
||||||
raster.set(px, py, color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function raster.fillEllipse(x1, y1, x2, y2, color)
|
|
||||||
local centerX = (x1 + x2) / 2
|
|
||||||
local centerY = (y1 + y2) / 2
|
|
||||||
|
|
||||||
local a = math.abs(x2 - x1) / 2
|
|
||||||
local b = math.abs(y2 - y1) / 2
|
|
||||||
|
|
||||||
centerX = math.floor(centerX + 0.5)
|
|
||||||
centerY = math.floor(centerY + 0.5)
|
|
||||||
a = math.floor(a + 0.5)
|
|
||||||
b = math.floor(b + 0.5)
|
|
||||||
|
|
||||||
if a <= 0 or b <= 0 then return end
|
|
||||||
|
|
||||||
if a == b then
|
|
||||||
raster.fillCircle(centerX, centerY, a, color)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local minX = centerX - a
|
|
||||||
local maxX = centerX + a
|
|
||||||
local minY = centerY - b
|
|
||||||
local maxY = centerY + b
|
|
||||||
|
|
||||||
for y = minY, maxY do
|
|
||||||
for x = minX, maxX do
|
|
||||||
local dx = x - centerX
|
|
||||||
local dy = y - centerY
|
|
||||||
local value = (dx*dx)/(a*a) + (dy*dy)/(b*b)
|
|
||||||
|
|
||||||
if value <= 1 then
|
|
||||||
raster.set(x, y, color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return raster
|
|
||||||
|
|||||||
+1
-125
@@ -1,125 +1 @@
|
|||||||
local serialize = {}
|
Error: no such file. (``)
|
||||||
|
|
||||||
function serialize.string(str)
|
|
||||||
return '"'..str:gsub("[%z\1-\31\34\92\127-\159]",function(c)
|
|
||||||
local byte = c:byte()
|
|
||||||
if byte== 7 then return "\\a" end
|
|
||||||
if byte== 8 then return "\\b" end
|
|
||||||
if byte== 9 then return "\\t" end
|
|
||||||
if byte==10 then return "\\n" end
|
|
||||||
if byte==11 then return "\\v" end
|
|
||||||
if byte==12 then return "\\f" end
|
|
||||||
if byte==13 then return "\\r" end
|
|
||||||
if byte==34 then return "\\\"" end
|
|
||||||
if byte==92 then return "\\\\" end
|
|
||||||
return string.format("\\x%02x",byte)
|
|
||||||
end)..'"'
|
|
||||||
end
|
|
||||||
|
|
||||||
function serialize.table(tbl,colors,stack)
|
|
||||||
stack = table.copy(stack or {})
|
|
||||||
table.insert(stack,tbl)
|
|
||||||
local keyAmount = 0
|
|
||||||
local keyNumber = true
|
|
||||||
local out = ""
|
|
||||||
local first = true
|
|
||||||
|
|
||||||
for key,val in pairs(tbl) do
|
|
||||||
if not first then out=out..",\n" end
|
|
||||||
first=false
|
|
||||||
out=out.." "
|
|
||||||
if type(key)=="string" then
|
|
||||||
if key:match("^[%a_][%w_]*$") then
|
|
||||||
out=out..key.."="
|
|
||||||
else
|
|
||||||
out=out..'['..serialize.string(key)..']='
|
|
||||||
end
|
|
||||||
else
|
|
||||||
out=out.."["..tostring(key).."]="
|
|
||||||
end
|
|
||||||
if type(key)~="number" then
|
|
||||||
keyNumber=false
|
|
||||||
end
|
|
||||||
|
|
||||||
local success,reason = pcall(function()
|
|
||||||
local valStr = ""
|
|
||||||
if type(val)=="table" then
|
|
||||||
if #stack>4 or table.find(stack,val) then
|
|
||||||
valStr="..."
|
|
||||||
else
|
|
||||||
valStr=serialize.table(val,colors,stack)
|
|
||||||
end
|
|
||||||
elseif type(val)=="string" then
|
|
||||||
local lines = {}
|
|
||||||
for line in (val.."\n"):gmatch("([^\n]*)\n") do table.insert(lines,line) end
|
|
||||||
if #lines[#lines]==0 then
|
|
||||||
lines[#lines]=nil
|
|
||||||
lines[#lines]=lines[#lines].."\n"
|
|
||||||
end
|
|
||||||
for i=1,#lines do
|
|
||||||
if i<#lines then
|
|
||||||
lines[i]=serialize.string(lines[i].."\n")
|
|
||||||
else
|
|
||||||
lines[i]=serialize.string(lines[i])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
valStr=table.concat(lines," ..\n ")
|
|
||||||
else
|
|
||||||
valStr=tostring(val)
|
|
||||||
end
|
|
||||||
local lines = {}
|
|
||||||
for line in (valStr.."\n"):gmatch("([^\n]*)\n") do table.insert(lines,line) end
|
|
||||||
out=out..table.concat(lines,"\n ")
|
|
||||||
lines = nil
|
|
||||||
keyAmount=keyAmount+1
|
|
||||||
end)
|
|
||||||
if not success then
|
|
||||||
if colors then out=out.."\x1b[91m" end
|
|
||||||
out=out.."["..tostring(reason).."]"
|
|
||||||
if colors then out=out.."\x1b[39m" end
|
|
||||||
end
|
|
||||||
coroutine.yield()
|
|
||||||
end
|
|
||||||
|
|
||||||
local metatbl = getmetatable(tbl)
|
|
||||||
local metakeys = {}
|
|
||||||
if type(metatbl)=="table" then
|
|
||||||
for i,v in pairs(metatbl) do
|
|
||||||
keyNumber=false
|
|
||||||
table.insert(metakeys,i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #metakeys>0 then
|
|
||||||
out=out.."\n "
|
|
||||||
if colors then out=out.."\x1b[92m" end
|
|
||||||
if table.find(metakeys,"__tostring") then
|
|
||||||
out=out.."tostring: "..serialize.string(tostring(tbl)).."\n "
|
|
||||||
table.remove(metakeys,table.find(metakeys,"__tostring"))
|
|
||||||
end
|
|
||||||
out=out..table.concat(metakeys,", ")
|
|
||||||
if colors then out=out.."\x1b[39m" end
|
|
||||||
end
|
|
||||||
|
|
||||||
if keyAmount==0 then return "{}" end
|
|
||||||
if keyNumber then
|
|
||||||
-- fix strings not being serialised
|
|
||||||
local vals = {}
|
|
||||||
for _,v in pairs(tbl) do
|
|
||||||
if #vals>=5 and #stack>1 then
|
|
||||||
table.insert(vals,"...")
|
|
||||||
break
|
|
||||||
end
|
|
||||||
if type(v)=="table" then
|
|
||||||
table.insert(vals,serialize.table(v,colors,stack))
|
|
||||||
elseif type(v)=="string" then
|
|
||||||
table.insert(vals,serialize.string(v))
|
|
||||||
else
|
|
||||||
table.insert(vals,tostring(v))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return "{"..table.concat(vals,", ").."}"
|
|
||||||
end
|
|
||||||
return "{\n"..out.."\n}"
|
|
||||||
end
|
|
||||||
|
|
||||||
return serialize
|
|
||||||
|
|||||||
+1
-102
@@ -1,102 +1 @@
|
|||||||
local unicodeLib
|
Error: no such file. (``)
|
||||||
local LLunicode
|
|
||||||
if table.copy then
|
|
||||||
unicodeLib = table.copy(unicode)
|
|
||||||
LLunicode = table.copy(unicode)
|
|
||||||
else
|
|
||||||
unicodeLib = {}
|
|
||||||
LLunicode = unicode
|
|
||||||
end
|
|
||||||
|
|
||||||
function unicodeLib.readCodePoint(readByte)
|
|
||||||
checkArg(1,readByte,"function")
|
|
||||||
|
|
||||||
local function inRange(min,max,...)
|
|
||||||
for _,v in ipairs({...}) do
|
|
||||||
if not (v and v>=min and v<max) then return false end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local byte = readByte()
|
|
||||||
if byte==nil then return end
|
|
||||||
|
|
||||||
if byte < 0x80 then
|
|
||||||
-- ASCII character (0xxxxxxx)
|
|
||||||
return byte
|
|
||||||
elseif byte < 0xC0 then
|
|
||||||
-- Continuation byte (10xxxxxx), invalid at start position
|
|
||||||
return nil
|
|
||||||
elseif byte < 0xE0 then
|
|
||||||
-- 2-byte sequence (110xxxxx 10xxxxxx)
|
|
||||||
local byte2 = readByte()
|
|
||||||
if byte2==nil then return nil end
|
|
||||||
if inRange(0x80,0xC0,byte2) then
|
|
||||||
local code_point = ((byte & 0x1F) << 6) | (byte2 & 0x3F)
|
|
||||||
return code_point
|
|
||||||
end
|
|
||||||
elseif byte < 0xF0 then
|
|
||||||
-- 3-byte sequence (1110xxxx 10xxxxxx 10xxxxxx)
|
|
||||||
local byte2, byte3 = readByte(), readByte()
|
|
||||||
if byte2==nil and byte3==nil then return nil end
|
|
||||||
if inRange(0x80,0xC0,byte2,byte3)then
|
|
||||||
local code_point = ((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F)
|
|
||||||
return code_point
|
|
||||||
end
|
|
||||||
elseif byte < 0xF8 then
|
|
||||||
-- 4-byte sequence (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
|
|
||||||
local byte2, byte3, byte4 = readByte(), readByte(), readByte()
|
|
||||||
if byte2==nil and byte3==nil and byte4==nil then return nil end
|
|
||||||
if inRange(0x80,0xC0,byte2,byte3,byte4) then
|
|
||||||
local code_point = ((byte & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F)
|
|
||||||
return code_point
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Invalid UTF-8 byte sequence
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function unicodeLib.readChar(readByte)
|
|
||||||
checkArg(1,readByte,"function")
|
|
||||||
return LLunicode.char(unicodeLib.readCodePoint(readByte))
|
|
||||||
end
|
|
||||||
|
|
||||||
function unicodeLib.codepoint(chr)
|
|
||||||
checkArg(1,chr,"string")
|
|
||||||
local ptr = 1
|
|
||||||
return unicode.readCodePoint(function()
|
|
||||||
local byte = chr:byte(ptr)
|
|
||||||
ptr=ptr+1
|
|
||||||
return byte
|
|
||||||
end),ptr-1
|
|
||||||
end
|
|
||||||
|
|
||||||
function unicodeLib.iterate(readByte)
|
|
||||||
checkArg(1,readByte,"string","function")
|
|
||||||
if type(readByte)=="string" then
|
|
||||||
local str,ptr = readByte,0
|
|
||||||
readByte = function()
|
|
||||||
ptr=ptr+1
|
|
||||||
return str:byte(ptr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return function()
|
|
||||||
local point = unicodeLib.readCodePoint(readByte)
|
|
||||||
if point==nil then return nil end
|
|
||||||
return LLunicode.char(point),point
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
unicodeLib.char = LLunicode.char
|
|
||||||
unicodeLib.charWidth = LLunicode.charWidth
|
|
||||||
unicodeLib.isWide = LLunicode.isWide
|
|
||||||
unicodeLib.len = LLunicode.len
|
|
||||||
unicodeLib.lower = LLunicode.lower
|
|
||||||
unicodeLib.reverse = LLunicode.reverse
|
|
||||||
unicodeLib.sub = LLunicode.sub
|
|
||||||
unicodeLib.upper = LLunicode.upper
|
|
||||||
unicodeLib.wlen = LLunicode.wlen
|
|
||||||
unicodeLib.wtrunc = LLunicode.wtrunc
|
|
||||||
|
|
||||||
return unicodeLib
|
|
||||||
|
|||||||
@@ -1,53 +1 @@
|
|||||||
local gpu = component.proxy(component.list("gpu")())
|
Error: no such file. (``)
|
||||||
local resx, resy = gpu.getResolution()
|
|
||||||
|
|
||||||
local function loadfile(file)
|
|
||||||
checkArg(1, file, "string")
|
|
||||||
local handle = component.invoke(computer.getBootAddress(), "open", file, "r")
|
|
||||||
local data = ""
|
|
||||||
repeat
|
|
||||||
local tmpdata = component.invoke(computer.getBootAddress(), "read", handle, math.huge or math.maxinteger)
|
|
||||||
data = data .. (tmpdata or "")
|
|
||||||
until not tmpdata
|
|
||||||
component.invoke(computer.getBootAddress(), "close", handle)
|
|
||||||
return assert(load(data, "=" .. file))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function handleError(errorMessage)
|
|
||||||
return(errorMessage.."\n \n"..debug.traceback())
|
|
||||||
end
|
|
||||||
|
|
||||||
function loadthething()
|
|
||||||
loadfile("/halyde/core/boot.lua")(loadfile)
|
|
||||||
end
|
|
||||||
|
|
||||||
gpu.setBackground(0x000000)
|
|
||||||
gpu.fill(1, 1, resx, resy, " ")
|
|
||||||
local result, reason = xpcall(loadthething, handleError)
|
|
||||||
if not result then
|
|
||||||
if import then
|
|
||||||
local computer = import("computer")
|
|
||||||
end
|
|
||||||
gpu.setBackground(0x000000)
|
|
||||||
gpu.fill(1, 1, resx, resy, " ")
|
|
||||||
gpu.setBackground(0x800000)
|
|
||||||
gpu.setForeground(0xFFFFFF)
|
|
||||||
gpu.set(2,2,"A critical error has occurred.")
|
|
||||||
local i = 4
|
|
||||||
reason = reason:gsub("\t", " ")
|
|
||||||
for line in string.gmatch((reason ~= nil and tostring(reason)) or "unknown error", "([^\n]*)\n?") do
|
|
||||||
gpu.set(2,i,line)
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
if computer~=nil then
|
|
||||||
gpu.set(2,i+1, "Press any key to restart.")
|
|
||||||
local evname
|
|
||||||
repeat
|
|
||||||
evname = computer.pullSignal()
|
|
||||||
until evname == "key_down"
|
|
||||||
computer.shutdown(true)
|
|
||||||
end
|
|
||||||
while true do
|
|
||||||
coroutine.yield()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
+1
-308
@@ -1,308 +1 @@
|
|||||||
local io = require("io")
|
Error: no such file. (``)
|
||||||
local component = require("component")
|
|
||||||
if not component.isAvailable("internet") then
|
|
||||||
io.stderr.write("This program requires an internet card to run.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
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
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
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 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]
|
|
||||||
installAddress = driveAddresses[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
|
|
||||||
end
|
|
||||||
if not installLocation 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)
|
|
||||||
local request, data, tmpdata = nil, "", nil
|
|
||||||
local status, errorMessage = pcall(function()
|
|
||||||
request = internet.request(url)
|
|
||||||
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 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()
|
|
||||||
log("Looking for outdated files in the drive")
|
|
||||||
progress("Preparing",1)
|
|
||||||
local oldFiles = {}
|
|
||||||
for oldFile in fs.list(installLocation) do
|
|
||||||
local usedFlag = false
|
|
||||||
for i = 1, 3 do
|
|
||||||
for _, file in pairs(webInstallConfig[installationOrder[i]].files) do
|
|
||||||
if oldFile == file then
|
|
||||||
usedFlag = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if webInstallConfig[installationOrder[i]].directories then
|
|
||||||
for _, dir in pairs(webInstallConfig[installationOrder[i]].directories) do
|
|
||||||
if oldFile == dir .. "/" then
|
|
||||||
usedFlag = true
|
|
||||||
end
|
|
||||||
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
|
|
||||||
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 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 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)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user