Rewrite everything

This commit is contained in:
Alessandro Proto 2023-02-09 22:17:17 +01:00
parent bcb358ed9d
commit 8b5fa96a74
38 changed files with 469 additions and 4154 deletions

View file

@ -1,17 +0,0 @@
local args = {...}
local fs = require("fs")
local dir = args[1]
if not dir then
dir = shell.homePath
end
dir = shell.resolve(dir)
if not fs.isDir(dir) then
error("No such directory: " .. dir, 0)
return false
end
shell.setDir(dir)

View file

@ -1,5 +0,0 @@
local term = require("term")
local colors = require("colors")
term.setBackground(colors.black)
term.clear()
term.setPos(1, 1)

View file

@ -1 +0,0 @@
shell.exit()

View file

@ -1,94 +0,0 @@
-- Mandelbrot in Capy64
local gpu = require("gpu")
local timer = require("timer")
local event = require("event")
local term = require("term")
-- lower = closer = slower
local scale = 4
-- higher = more detailed = slower
local iterations = 100
local pscale = scale
local w, h = gpu.getSize()
local px = pscale / w
local colorUnit = math.floor(0xffffff / iterations)
-- todo: make it interactive
local dx, dy = 0, 0
local cx, cy = math.floor(w / 2), math.floor(h / 2)
-- z = z^2 + c
local function mandelbrot(zr, zi, cr, ci)
return zr ^ 2 - zi ^ 2 + cr,
2 * zr * zi + ci
end
local function iter(cr, ci)
local zr, zi = 0, 0
for i = 1, iterations do
zr, zi = mandelbrot(zr, zi, cr, ci)
if math.abs(zr) >= (pscale >= 2 and pscale or 2) or math.abs(zi) >= (pscale >= 2 and pscale or 2) then
return false, colorUnit, i
end
end
return true, 0, iterations
end
local function draw()
local buffer <close> = gpu.newBuffer()
for y = 0, h - 1 do
for x = 0, w - 1 do
local _, _, i = iter((x - cx + dx * pscale) * px, (y - cy + dy * pscale) * px)
buffer[y * w + x] = colorUnit * (iterations - i)
end
end
gpu.setBuffer(buffer)
end
-- no idea why it's needed
timer.sleep(1)
draw()
local tw, th = term.getSize()
while true do
term.setPos(1, th)
term.setBackground(0)
term.setForeground(0xffffff)
term.write("X: " .. dx .. "; Y: " .. dy .. "; S: " .. pscale .. "; " .. px .. "!")
local ev = { event.pull("key_down") }
if ev[1] == "key_down" then
local key = ev[3]
if key == "up" then
dy = dy - 10 / pscale
elseif key == "down" then
dy = dy + 10 / pscale
elseif key == "right" then
dx = dx + 10 / pscale
elseif key == "left" then
dx = dx - 10 / pscale
elseif key == "enter" then
draw()
elseif key == "page_down" then
pscale = pscale * 1.25
dx = dx * pscale
dy = dy * pscale
elseif key == "page_up" then
pscale = pscale / 1.25
dx = dx / pscale
dy = dy / pscale
elseif key == "r" then
pscale = scale
dx = 0
dy = 0
end
end
px = pscale / w
end

View file

@ -1,73 +0,0 @@
local gpu = require("gpu")
local timer = require("timer")
local event = require("event")
local colors = require("colors")
local parallel = require("parallel")
local melts = 2 ^ 12
local function contains(arr, val)
for k, v in ipairs(arr) do
if v == val then
return true
end
end
return false
end
local function melt()
local w, h = gpu.getSize()
local x, y = 0, 0
while true do
local buffer <close> = gpu.getBuffer()
for i = 1, melts do
local nx = math.random(x, w)
local ny = math.random(y, h)
local c = buffer[ny * w + nx]
buffer[(ny + 1) * w + nx] = c
end
gpu.setBuffer(buffer)
timer.sleep(10)
end
end
local function draw()
local ox, oy
while true do
local ev, b, x, y = event.pull("mouse_move", "mouse_down")
if ev == "mouse_down" then
if b == 1 then
ox = x
oy = y
end
elseif ev == "mouse_move" then
if contains(b, 1) then
gpu.plot(x, y, colors.red)
gpu.drawLine(x, y, ox, oy, colors.red)
ox, oy = x, y
end
end
end
end
local function random()
local w, h = gpu.getSize()
while true do
for i = 1, 24 do
gpu.drawString(
math.random(-7, w),
math.random(-13, h),
math.random(0, 0xffffff),
string.char(math.random(32, 127))
)
end
timer.sleep(100)
end
end
parallel.waitForAny(draw, melt, random)

View file

@ -1,17 +0,0 @@
local timer = require("timer")
local colors = require("colors")
local term = require("term")
local function slowPrint(text, delay)
for i = 1, #text do
local ch = text:sub(i, i)
io.write(ch)
timer.sleep(delay)
end
print()
end
local color = colors[math.random(1, #colors)]
term.setForeground(color)
slowPrint("Hello, World!", 50)

View file

@ -1,25 +0,0 @@
local args = { ... }
local fs = require("fs")
local term = require("term")
local colors = require("colors")
local dir = shell.getDir()
if args[1] then
dir = shell.resolve(args[1])
end
if not fs.isDir(dir) then
error("No such directory: " .. dir, 0)
return false
end
local files = fs.list(dir)
for k, v in ipairs(files) do
if fs.isDir(fs.combine(dir, v)) then
term.setForeground(colors.lightBlue)
print(v .. "/")
else
term.setForeground(colors.white)
print(v)
end
end

View file

@ -1,75 +0,0 @@
local term = require("term")
local io = require("io")
local colors = require("colors")
local colours = colors
local tArgs = { ... }
if #tArgs > 0 then
print("This is an interactive Lua prompt.")
print("To run a lua program, just type its name.")
return
end
--local pretty = require "cc.pretty"
local bRunning = true
local tCommandHistory = {}
local tEnv = {
["exit"] = setmetatable({}, {
__tostring = function() return "Call exit() to exit." end,
__call = function() bRunning = false end,
}),
}
setmetatable(tEnv, { __index = _ENV })
for k, v in pairs(package.loaded) do
tEnv[k] = v
end
term.setForeground(colours.yellow)
print(_VERSION .. " interactive prompt")
print("Call exit() to exit.")
term.setForeground(colours.white)
while bRunning do
term.setForeground(colours.yellow)
io.write("> ")
term.setForeground(colours.white)
local s = io.read(nil, tCommandHistory)
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
table.insert(tCommandHistory, s)
end
local nForcePrint = 0
local func, e = load(s, "=lua", "t", tEnv)
local func2 = load("return " .. s, "=lua", "t", tEnv)
if not func then
if func2 then
func = func2
e = nil
nForcePrint = 1
end
else
if func2 then
func = func2
end
end
if func then
local tResults = table.pack(pcall(func))
if tResults[1] then
local n = 1
while n < tResults.n or n <= nForcePrint do
local value = tResults[n + 1]
print(tostring(value))
n = n + 1
end
else
io.stderr.print(tResults[2])
end
else
io.stderr.print(e)
end
end

View file

@ -1,14 +0,0 @@
local fs = require("fs")
local args = { ... }
if #args == 0 then
print("Usage: mkdir <directory>")
return
end
local dir = shell.resolve(args[1])
if fs.exists(dir) then
error("Path already exists", 0)
end
fs.makeDir(dir)

View file

@ -1 +0,0 @@
print(shell.getDir())

View file

@ -1,8 +0,0 @@
local timer = require("timer")
local machine = require("machine")
print("Goodbye!")
timer.sleep(1000)
machine.reboot()

View file

@ -1,10 +0,0 @@
local fs = require("fs")
local args = { ... }
if #args == 0 then
print("Usage: rm <file>")
return
end
local file = shell.resolve(args[1])
fs.delete(file, true)

View file

@ -1,143 +0,0 @@
local term = require("term")
local colors = require("colors")
local fs = require("fs")
local machine = require("machine")
local exit = false
local shell = {}
shell.path = "./?;./?.lua;/bin/?.lua"
shell.homePath = "/home"
local currentDir = shell.homePath
local function buildEnvironment(path, args)
local arg = { table.unpack(args, 2) }
arg[0] = path
return setmetatable({
shell = shell,
arg = arg
}, { __index = _G })
end
local function tokenise(...)
local sLine = table.concat({ ... }, " ")
local tWords = {}
local bQuoted = false
for match in string.gmatch(sLine .. "\"", "(.-)\"") do
if bQuoted then
table.insert(tWords, match)
else
for m in string.gmatch(match, "[^ \t]+") do
table.insert(tWords, m)
end
end
bQuoted = not bQuoted
end
return tWords
end
function shell.getDir()
return currentDir
end
function shell.setDir(path)
currentDir = path
end
function shell.resolve(path)
if path:sub(1, 1) == "/" then
return fs.combine("", path)
end
if path:sub(1, 1) == "~" then
return fs.combine(shell.homePath, path)
end
return fs.combine(currentDir, path)
end
function shell.resolveProgram(path)
if path:sub(1, 1) == "/" then
return shell.resolve(path)
end
for seg in shell.path:gmatch("[^;]+") do
local resolved = shell.resolve(seg:gsub("%?", path))
if fs.exists(resolved) and not fs.isDir(resolved) then
return resolved
end
end
end
function shell.run(...)
local args = tokenise(...)
local command = args[1]
local path = shell.resolveProgram(command)
if not path then
io.stderr.print("Command not found: " .. command)
return false
end
local env = buildEnvironment(command, args)
local func, err = loadfile(path, "t", env)
if not func then
io.stderr.print(err)
return false
end
local ok, err = pcall(func, table.unpack(args, 2))
if not ok then
io.stderr.print(err)
return false
end
return true
end
function shell.exit()
exit = true
end
if not fs.exists(shell.homePath) then
fs.makeDir(shell.homePath)
end
local history = {}
local lastExecSuccess = true
while not exit do
machine.setRPC(os.version(), "On shell")
term.setBackground(colors.black)
term.setForeground(colors.white)
io.write(":")
term.setForeground(colors.lightBlue)
if currentDir == shell.homePath then
io.write("~")
else
io.write(currentDir)
end
if lastExecSuccess then
term.setForeground(colors.yellow)
else
term.setForeground(colors.red)
end
io.write("$ ")
term.setForeground(colors.white)
local line = io.read(nil, history)
if line:match("%S") and history[#history] ~= line then
table.insert(history, line)
end
if line:match("%S") then
machine.setRPC(os.version(), "Running: " .. line)
lastExecSuccess = shell.run(line)
end
end

View file

@ -1,8 +0,0 @@
local timer = require("timer")
local machine = require("machine")
print("Goodbye!")
timer.sleep(1000)
machine.shutdown()

View file

@ -1 +0,0 @@
print(string.format("%s @ %s - %s", os.version(), _HOST, _VERSION))

View file

@ -1,34 +0,0 @@
local http = require("http")
local fs = require("fs")
local args = { ... }
if not http then
error("HTTP is not enabled", 0)
end
if #args == 0 then
print("Usage: wget <url> [outputPath]")
return false
end
local outputName = args[2] or fs.getName(args[1])
local outputPath = shell.resolve(outputName)
if not http.checkURL(args[1]) then
error("Invalid URL", 0)
end
local response <close>, err = http.get(args[1], nil, {
binary = true,
})
if not response then
error(err, 0)
end
local file <close> = fs.open(outputPath, "wb")
file:write(response:readAll())
file:close()
response:close()
print("File written to " .. outputPath)

View file

@ -1,3 +0,0 @@
package.path = "/lib/?.lua;/lib/?/init.lua;" .. package.path
_G.io = require("io")

View file

@ -1,15 +0,0 @@
function _G.print(...)
local args = { ... }
local size = #args
local lines = 0
for n, v in ipairs(args) do
local s = tostring(v)
if n < size then
s = s .. "\t"
end
lines = lines + io.write(s)
end
lines = lines + io.write("\n")
return lines
end

View file

@ -1,90 +0,0 @@
local http = require("http")
local event = require("event")
local expect = require("expect").expect
function http.request(url, body, headers, options)
expect(1, url, "string")
expect(2, body, "string", "nil")
expect(3, headers, "table", "nil")
expect(4, options, "table", "nil")
if not http.checkURL(url) then
return nil, "Invalid URL"
end
local requestId = http.requestAsync(url, body, headers, options)
local ev, id, data, info
repeat
ev, id, data, info = event.pull("http_response", "http_failure")
until id == requestId
if ev == "http_failure" then
return nil, data
end
return data, info
end
function http.get(url, headers, options)
expect(1, url, "string")
expect(2, headers, "table", "nil")
expect(3, options, "table", "nil")
return http.request(url, nil, headers, options)
end
function http.post(url, body, headers, options)
expect(1, url, "string")
expect(2, body, "string", "nil")
expect(3, headers, "table", "nil")
expect(4, options, "table", "nil")
return http.request(url, body, headers, options)
end
local WebSocketHandle
local function buildWebsocketHandle(handle)
if not WebSocketHandle then
WebSocketHandle = getmetatable(handle) or { __index = {} }
function WebSocketHandle.__index:close()
self:closeAsync()
local _, id
repeat
_, id = event.pull("websocket_close")
until id == self:getRequestID()
end
function WebSocketHandle.__index:receive()
local _, id, par
repeat
_, id, par = event.pull("websocket_message")
until id == self:getRequestID()
return par
end
end
return handle
end
function http.websocket(url, headers)
expect(1, url, "string")
expect(2, headers, "table", "nil")
if not http.checkURL(url) then
return nil, "Invalid URL"
end
local requestId = http.websocketAsync(url, headers)
local ev, id, par
repeat
ev, id, par = event.pull("websocket_connect", "websocket_failure")
until id == requestId
if ev == "http_failure" then
return nil, par
end
return buildWebsocketHandle(par)
end

View file

@ -1,14 +0,0 @@
local timer = require("timer")
local event = require("event")
local expect = require("expect").expect
local range = require("expect").range
function timer.sleep(n)
expect(1, n, "number")
range(1, 1)
local timerId = timer.start(n)
repeat
local _, par = event.pull("timer")
until par == timerId
end

View file

@ -1,15 +0,0 @@
local term = require("term")
local colors = require("colors")
local machine = require("machine")
term.setForeground(0x59c9ff)
term.setBackground(colors.black)
term.clear()
term.setPos(1, 1)
term.write(os.version())
term.setPos(1, 2)
dofile("/bin/shell.lua")
machine.shutdown()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View file

@ -1,47 +0,0 @@
local version = "0.0.2"
print("Starting CapyOS")
local term = require("term")
local fs = require("fs")
local gpu = require("gpu")
local nPrint = print
local function showError(err)
nPrint(err)
local x, y = term.getPos()
term.setForeground(0xff0000)
term.setPos(1, y)
term.write(err)
term.setPos(1, y + 1)
term.setForeground(0xffffff)
term.write("Press any key to continue")
coroutine.yield("key_down")
end
function os.version()
return "CapyOS " .. version
end
term.setSize(51, 19)
gpu.setScale(2)
term.setPos(1, 1)
term.write(_HOST)
local files = fs.list("/boot/autorun")
for i = 1, #files do
local func, err = loadfile("/boot/autorun/" .. files[i])
if not func then
showError(err)
break
end
local ok, err = pcall(func)
if not ok then
showError(debug.traceback(err))
break
end
end

View file

@ -1,94 +0,0 @@
local expect = require("expect")
local palette = {
{
"white",
0xf0f0f0
},
{
"orange",
0xf2b233
},
{
"magenta",
0xe57fd8
},
{
"lightBlue",
0x99b2f2
},
{
"yellow",
0xdede6c
},
{
"lime",
0x7fcc19
},
{
"pink",
0xf2b2cc
},
{
"gray",
0x4c4c4c
},
{
"lightGray",
0x999999
},
{
"cyan",
0x4c99b2
},
{
"purple",
0xb266e5
},
{
"blue",
0x3366cc
},
{
"brown",
0x7f664c
},
{
"green",
0x57a64e
},
{
"red",
0xcc4c4c
},
{
"black",
0x111111
}
}
local colors = {}
for k, v in ipairs(palette) do
colors[v[1]] = v[2]
colors[k] = v[2]
end
function colors.packRGB(r, g, b)
expect(1, r, "number")
expect(2, g, "number")
expect(3, b, "number")
return (r << 16) +
(g << 8) +
b
end
function colors.unpackRGB(rgb)
expect(1, rgb, "number")
return (rgb >> 16) & 0xff,
(rgb >> 8) & 0xff,
rgb & 0xff
end
return colors;

View file

@ -1,52 +0,0 @@
-- Credits: https://github.com/Ocawesome101/recrafted
-- cc.expect
local _expect = {}
local function checkType(index, valueType, value, ...)
local expected = table.pack(...)
local isType = false
for i = 1, expected.n, 1 do
if type(value) == expected[i] then
isType = true
break
end
end
if not isType then
error(string.format("bad %s %s (%s expected, got %s)", valueType,
index, table.concat(expected, " or "), type(value)), 3)
end
return value
end
function _expect.expect(index, value, ...)
return checkType(("#%d"):format(index), "argument", value, ...)
end
function _expect.field(tbl, index, ...)
_expect.expect(1, tbl, "table")
_expect.expect(2, index, "string")
return checkType(("%q"):format(index), "field", tbl[index], ...)
end
function _expect.range(num, min, max)
_expect.expect(1, num, "number")
_expect.expect(2, min, "number", "nil")
_expect.expect(3, max, "number", "nil")
min = min or -math.huge
max = max or math.huge
if num < min or num > max then
error(("number outside of range (expected %d to be within %d and %d")
:format(num, min, max), 2)
end
end
setmetatable(_expect, { __call = function(_, ...)
return _expect.expect(...)
end })
return _expect

View file

@ -1,282 +0,0 @@
local expect = require("expect").expect
local event = require("event")
local term = require("term")
local io = {}
function io.write(text)
text = tostring(text)
local lines = 0
local w, h = term.getSize()
local function inc_cy(cy)
lines = lines + 1
if cy > h - 1 then
term.scroll(1)
return cy
else
return cy + 1
end
end
while #text > 0 do
local nl = text:find("\n") or #text
local chunk = text:sub(1, nl)
text = text:sub(#chunk + 1)
local has_nl = chunk:sub(-1) == "\n"
if has_nl then chunk = chunk:sub(1, -2) end
local cx, cy = term.getPos()
while #chunk > 0 do
if cx > w then
term.setPos(1, inc_cy(cy))
cx, cy = term.getPos()
end
local to_write = chunk:sub(1, w - cx + 1)
term.write(to_write)
chunk = chunk:sub(#to_write + 1)
cx, cy = term.getPos()
end
if has_nl then
term.setPos(1, inc_cy(cy))
end
end
return lines
end
local empty = {}
function io.read(replace, history, complete, default)
expect(1, replace, "string", "nil")
expect(2, history, "table", "nil")
expect(3, complete, "function", "nil")
expect(4, default, "string", "nil")
if replace then replace = replace:sub(1, 1) end
local hist = history or {}
history = {}
for i = 1, #hist, 1 do
history[i] = hist[i]
end
local buffer = default or ""
local prev_buf = buffer
history[#history + 1] = buffer
local hist_pos = #history
local cursor_pos = 0
local stx, sty = term.getPos()
local w, h = term.getSize()
local dirty = false
local completions = {}
local comp_id = 0
local function clearCompletion()
if completions[comp_id] then
write((" "):rep(#completions[comp_id]))
end
end
local function full_redraw(force)
if force or dirty then
if complete and buffer ~= prev_buf then
completions = complete(buffer) or empty
comp_id = math.min(1, #completions)
end
prev_buf = buffer
term.setPos(stx, sty)
local text = buffer
if replace then text = replace:rep(#text) end
local ln = io.write(text)
if completions[comp_id] then
local oldfg = term.getForeground()
local oldbg = term.getBackground()
term.setForeground(colors.white)
term.setBackground(colors.gray)
ln = ln + write(completions[comp_id])
term.setForeground(oldfg)
term.setBackground(oldbg)
else
ln = ln + io.write(" ")
end
if sty + ln > h then
sty = sty - (sty + ln - h)
end
end
-- set cursor to the appropriate spot
local cx, cy = stx, sty
cx = cx + #buffer - cursor_pos -- + #(completions[comp_id] or "")
while cx > w do
cx = cx - w
cy = cy + 1
end
term.setPos(cx, cy)
end
term.setBlink(true)
while true do
full_redraw()
-- get input
local evt, par1, par2 = event.pull()
if evt == "char" then
dirty = true
clearCompletion()
if cursor_pos == 0 then
buffer = buffer .. par1
elseif cursor_pos == #buffer then
buffer = par1 .. buffer
else
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub(-cursor_pos)
end
elseif evt == "paste" then
dirty = true
clearCompletion()
if cursor_pos == 0 then
buffer = buffer .. par1
elseif cursor_pos == #buffer then
buffer = par1 .. buffer
else
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 ..
buffer:sub(-cursor_pos + (#par1 - 1))
end
elseif evt == "key_down" then
if par2 == "back" and #buffer > 0 then
dirty = true
if cursor_pos == 0 then
buffer = buffer:sub(1, -2)
clearCompletion()
elseif cursor_pos < #buffer then
buffer = buffer:sub(0, -cursor_pos - 2) .. buffer:sub(-cursor_pos)
end
elseif par2 == "delete" and cursor_pos > 0 then
dirty = true
if cursor_pos == #buffer then
buffer = buffer:sub(2)
elseif cursor_pos == 1 then
buffer = buffer:sub(1, -2)
else
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
end
cursor_pos = cursor_pos - 1
elseif par2 == "up" then
if #completions > 1 then
dirty = true
clearCompletion()
if comp_id > 1 then
comp_id = comp_id - 1
else
comp_id = #completions
end
elseif hist_pos > 1 then
cursor_pos = 0
history[hist_pos] = buffer
hist_pos = hist_pos - 1
buffer = (" "):rep(#buffer)
full_redraw(true)
buffer = history[hist_pos]
dirty = true
end
elseif par2 == "down" then
if #completions > 1 then
dirty = true
clearCompletion()
if comp_id < #completions then
comp_id = comp_id + 1
else
comp_id = 1
end
elseif hist_pos < #history then
cursor_pos = 0
history[hist_pos] = buffer
hist_pos = hist_pos + 1
buffer = (" "):rep(#buffer)
full_redraw(true)
buffer = history[hist_pos]
dirty = true
end
elseif par2 == "left" then
if cursor_pos < #buffer then
clearCompletion()
cursor_pos = cursor_pos + 1
end
elseif par2 == "right" then
if cursor_pos > 0 then
cursor_pos = cursor_pos - 1
elseif comp_id > 0 then
dirty = true
buffer = buffer .. completions[comp_id]
end
elseif par2 == "tab" then
if comp_id > 0 then
dirty = true
buffer = buffer .. completions[comp_id]
end
elseif par2 == "home" then
cursor_pos = #buffer
elseif par2 == "end" then
cursor_pos = 0
elseif par2 == "enter" then
clearCompletion()
print()
break
end
end
end
term.setBlink(false)
return buffer
end
io.stderr = {}
function io.stderr.write(text)
local fg = term.getForeground()
term.setForeground(0xff0000)
io.write(text)
term.setForeground(fg)
end
function io.stderr.print(...)
local fg = term.getForeground()
term.setForeground(0xff0000)
print(...)
term.setForeground(fg)
end
return io

View file

@ -1,61 +0,0 @@
local expect = require("expect")
local parallel = {}
local function contains(array, value)
for k, v in pairs(array) do
if v == value then
return true
end
end
return false
end
local function run(threads, exitOnAny)
local alive = #threads
local filters = {}
local ev = {}
while true do
for i, thread in pairs(threads) do
if not filters[i] or #filters[i] == 0 or contains(filters[i], ev[1]) or ev[1] == "interrupt" then
local pars = table.pack(coroutine.resume(thread, table.unpack(ev)))
if pars[1] then
filters[i] = table.pack(table.unpack(pars, 2))
else
error(pars[2], 0)
end
end
if coroutine.status(thread) == "dead" then
alive = alive - 1
if exitOnAny or alive <= 0 then
return
end
end
end
ev = table.pack(coroutine.yield())
end
end
function parallel.waitForAll(...)
local threads = {}
for k, v in ipairs({ ... }) do
expect(k, v, "function")
table.insert(threads, coroutine.create(v))
end
return run(threads, false)
end
function parallel.waitForAny(...)
local threads = {}
for k, v in ipairs({ ... }) do
expect(k, v, "function")
table.insert(threads, coroutine.create(v))
end
return run(threads, true)
end
return parallel

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,14 @@ local gpu = require("gpu")
local fs = require("fs")
local machine = require("machine")
local audio = require("audio")
local http = require("http")
local event = require("event")
local BASE_URL = "https://api.github.com/repos/%s/contents/%s"
local REPOSITORY = "Capy64/CapyOS"
local JSON_URL = "https://raw.githubusercontent.com/Capy64/CapyOS/main/lib/json.lua"
-- This token only has read-only access to repository files
local GITHUB_TOKEN = "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"
local bootSleep = 2000
local bg = 0x0
@ -16,6 +24,8 @@ term.clear()
term.setSize(51, 19)
gpu.setScale(2)
local w, h = term.getSize()
local function sleep(n)
local timerId = timer.start(n)
repeat
@ -54,7 +64,96 @@ local function drawVendorImage()
end
end
local w, h = term.getSize()
local nPrint = print
local function print( text )
local x, y = term.getPos()
term.write(tostring(text))
if y == h then
term.scroll(1)
term.setPos(1, y)
else
term.setPos(1, y + 1)
end
end
local function printError( text )
term.setForeground(0xff0000)
print(text)
term.setForeground(0xffffff)
end
local function hget(url, headers)
local requestId = http.requestAsync(url, nil, headers, {binary = true})
local ev, rId, par, info
repeat
ev, rId, par, info = event.pull("http_response", "http_failure")
until rId == requestId
if ev == "http_failure" then
return nil, par
end
local content = par:read("a")
par:close()
return content, info
end
local function promptKey()
print("Press any key to continue")
event.pull("key_down")
end
local function repo(path)
return string.format(BASE_URL, REPOSITORY, path or "")
end
local json
local function recursiveDownload(path)
path = path or ""
local index = json.decode(hget(repo(path), {
Authorization = "Bearer " .. GITHUB_TOKEN
}))
for i, v in ipairs(index) do
if v.type == "dir" then
if not fs.exists(v.path) then
fs.makeDir(v.path)
end
recursiveDownload(v.path)
elseif v.type == "file" then
print("Downloading " .. v.path)
local fileContent = hget(v.download_url)
local f = fs.open(v.path, "wb")
f:write(fileContent)
f:close()
print("Written to " .. v.path)
end
end
end
local function installOS()
term.clear()
term.setPos(1, 1)
print("Installing CapyOS...")
local jsonLib, par = hget(JSON_URL)
if not jsonLib then
printError(par)
promptKey()
return
end
json = load(jsonLib)()
recursiveDownload()
flagInstalled()
print("CapyOS installed!")
promptKey()
end
term.setBlink(false)
@ -148,6 +247,10 @@ end
audio.beep(1000, 0.4, 0.2)
if shouldInstallOS() then
installOS()
end
bootScreen()
term.clear()

View file

@ -1,11 +1,12 @@
using Capy64.API;
using Capy64.Runtime.Objects.Handlers;
using Capy64.Runtime.Extensions;
using KeraLua;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Capy64.Runtime.Objects;
namespace Capy64.Runtime.Libraries;
@ -491,8 +492,7 @@ public class FileSystem : IPlugin
{
var L = Lua.FromIntPtr(state);
var path = Resolve(L.CheckString(1));
var mode = L.CheckString(2);
var mode = L.OptString(2, "r");
var errorMessage = "invalid file mode";
if (mode.Length < 1 && mode.Length > 2)
@ -542,25 +542,8 @@ public class FileSystem : IPlugin
}
var fileStream = File.Open(path, fileMode, fileAccess, FileShare.ReadWrite);
if (fileAccess == FileAccess.Read)
{
if (binaryMode)
BinaryReadHandle.Push(L, new(fileStream));
else
ReadHandle.Push(L, new(fileStream));
}
else if (fileAccess == FileAccess.Write)
{
if (binaryMode)
BinaryWriteHandle.Push(L, new(fileStream));
else
WriteHandle.Push(L, new(fileStream));
}
else
{
throw new InvalidOperationException();
}
L.PushObject(fileStream);
L.SetMetaTable(FileHandle.ObjectType);
return 1;
}

View file

@ -1,10 +1,11 @@
using Capy64.API;
using Capy64.Runtime.Extensions;
using Capy64.Runtime.Objects.Handlers;
using Capy64.Runtime.Objects;
using KeraLua;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.WebSockets;
@ -209,10 +210,12 @@ public class HTTP : IPlugin
LK.PushInteger(requestId);
// arg 2, response data
if ((bool)options["binary"])
L.PushObject(stream);
L.SetMetaTable(FileHandle.ObjectType);
/*if ((bool)options["binary"])
BinaryReadHandle.Push(LK, new(stream));
else
ReadHandle.Push(LK, new(stream));
ReadHandle.Push(LK, new(stream));*/
// arg 3, response info
LK.NewTable();

View file

@ -0,0 +1,333 @@
using Capy64.API;
using KeraLua;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Capy64.Runtime.Objects;
public class FileHandle : IPlugin
{
public const string ObjectType = "file";
private static LuaRegister[] Methods = new LuaRegister[]
{
new()
{
name = "read",
function = L_Read,
},
new()
{
name = "write",
function = L_Write,
},
new()
{
name = "lines",
function = L_Lines,
},
new()
{
name = "flush",
function = L_Flush,
},
new()
{
name = "seek",
function = L_Seek,
},
new()
{
name = "close",
function = L_Close,
},
new(),
};
private static LuaRegister[] MetaMethods = new LuaRegister[]
{
new()
{
name = "__index",
},
new()
{
name = "__gc",
function = F_GC,
},
new()
{
name = "__close",
function = F_GC,
},
new()
{
name = "__tostring",
function = F_ToString,
},
new(),
};
public void LuaInit(Lua L)
{
CreateMeta(L);
}
public static void CreateMeta(Lua L)
{
L.NewMetaTable(ObjectType);
L.SetFuncs(MetaMethods, 0);
L.NewLibTable(Methods);
L.SetFuncs(Methods, 0);
L.SetField(-2, "__index");
L.Pop(1);
}
private static char CheckMode(string mode)
{
var modes = "nlLa";
var i = modes.IndexOf(mode.TrimStart('*')[0]);
return modes[i];
}
private static Stream ToStream(Lua L, bool gc = false)
{
return L.CheckObject<Stream>(1, ObjectType, gc);
}
private static Stream CheckStream(Lua L, bool gc = false)
{
var obj = L.CheckObject<Stream>(1, ObjectType, gc);
if (obj is null)
{
L.Error("attempt to use a closed file");
return null;
}
return obj;
}
private static bool ReadNumber(Lua L, Stream stream)
{
return false;
}
private static bool ReadLine(Lua L, Stream stream, bool chop)
{
var buffer = new byte[stream.Length];
int i = 0;
int c = 0;
for (; i < stream.Length; i++)
{
c = stream.ReadByte();
if (c == -1 || c == '\n')
break;
buffer[i] = (byte)c;
}
if (!chop && c == '\n')
buffer[i++] = (byte)c;
Array.Resize(ref buffer, i);
L.PushBuffer(buffer);
return c == '\n' || L.RawLen(-1) > 0;
}
private static void ReadAll(Lua L, Stream stream)
{
var buffer = new byte[stream.Length];
var read = stream.Read(buffer, 0, buffer.Length);
Array.Resize(ref buffer, read);
L.PushBuffer(buffer);
}
private static bool ReadChars(Lua L, Stream stream, int n)
{
var buffer = new byte[n];
var read = stream.Read(buffer, 0, n);
Array.Resize(ref buffer, read);
L.PushBuffer(buffer);
return read != 0;
}
private static int L_Read(IntPtr state)
{
var L = Lua.FromIntPtr(state);
bool success = false;
var stream = CheckStream(L);
if (!stream.CanRead)
{
L.PushNil();
L.PushString("Attempt to read from a write-only stream");
return 2;
}
var nargs = L.GetTop() - 1;
if (nargs == 0)
{
L.PushString("l");
}
if (L.Type(2) == LuaType.Number)
{
success = ReadChars(L, stream, (int)L.ToNumber(2));
}
else
{
var p = L.CheckString(2);
var mode = CheckMode(p);
switch (mode)
{
case 'n':
success = ReadNumber(L, stream);
break;
case 'l':
success = ReadLine(L, stream, true);
break;
case 'L':
success = ReadLine(L, stream, false);
break;
case 'a':
ReadAll(L, stream);
success = true;
break;
default:
return L.ArgumentError(2, "invalid format");
}
}
if (!success)
{
L.Pop(1);
L.PushNil();
}
return 1;
}
private static int L_Write(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var nargs = L.GetTop();
var stream = CheckStream(L);
if (!stream.CanWrite)
{
L.PushNil();
L.PushString("Attempt to write to a read-only stream");
return 2;
}
for (int arg = 2; arg <= nargs; arg++)
{
if (L.Type(arg) == LuaType.Number)
{
stream.WriteByte((byte)L.ToNumber(arg));
}
else
{
var buffer = L.CheckBuffer(arg);
stream.Write(buffer);
}
}
return L.FileResult(1, null);
}
private static int L_Lines(IntPtr state)
{
return 0;
var L = Lua.FromIntPtr(state);
var maxargn = 250;
var n = L.GetTop() - 1;
var stream = CheckStream(L, false);
L.ArgumentCheck(n <= maxargn, maxargn + 2, "too many arguments");
L.PushCopy(1);
L.PushInteger(n);
L.PushBoolean(false);
L.Rotate(2, 3);
L.PushCClosure(null, 3 + n); // todo
return 1;
}
private static int L_Flush(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = CheckStream(L, false);
stream.Flush();
return 0;
}
private static int L_Seek(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = CheckStream(L, false);
var whence = L.CheckOption(2, "cur", new[]
{
"set", // begin 0
"cur", // current 1
"end", // end 2
null,
});
var offset = L.OptInteger(3, 0);
var newPosition = stream.Seek(offset, (SeekOrigin)whence);
L.PushInteger(newPosition);
return 1;
}
private static int L_Close(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = CheckStream(L, true);
stream.Close();
return 0;
}
private static unsafe int F_ToString(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = ToStream(L);
if (stream is not null)
{
L.PushString("file ({0:X})", (ulong)&stream);
}
else
{
L.PushString("file (closed)");
}
return 1;
}
private static int F_GC(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = ToStream(L, true);
if (stream is not null)
stream.Close();
return 0;
}
}

View file

@ -1,508 +0,0 @@
using KeraLua;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Capy64.Runtime.Objects.Handlers;
public class BinaryReadHandle
{
public const string ObjectType = "BinaryReadHandle";
private static readonly Dictionary<string, LuaFunction> functions = new()
{
["readAll"] = L_ReadAll,
["seek"] = L_Seek,
["readByte"] = L_ReadByte,
["readShort"] = L_ReadShort,
["readInt"] = L_ReadInt,
["readLong"] = L_ReadLong,
["readSByte"] = L_ReadSByte,
["readUShort"] = L_ReadUShort,
["readUInt"] = L_ReadUInt,
["readULong"] = L_ReadULong,
["readHalf"] = L_ReadHalf,
["readFloat"] = L_ReadFloat,
["readDouble"] = L_ReadDouble,
["readChar"] = L_ReadChar,
["readString"] = L_ReadString,
["readBoolean"] = L_ReadBoolean,
["close"] = L_Close,
};
public static void Push(Lua L, BinaryReader stream)
{
L.PushObject(stream);
if (L.NewMetaTable(ObjectType))
{
L.PushString("__index");
L.NewTable();
foreach (var pair in functions)
{
L.PushString(pair.Key);
L.PushCFunction(pair.Value);
L.SetTable(-3);
}
L.SetTable(-3);
L.PushString("__close");
L.PushCFunction(L_Close);
L.SetTable(-3);
L.PushString("__gc");
L.PushCFunction(L_Close);
L.SetTable(-3);
}
L.SetMetaTable(-2);
}
private static int L_Read(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (L.IsInteger(2))
{
if (stream.BaseStream.Position >= stream.BaseStream.Length)
{
L.PushNil();
return 0;
}
stream.ReadChars((int)L.ToInteger(2));
}
else if (L.IsString(2))
{
var option = L.ToString(2);
option = option.TrimStart('*');
if (option.Length == 0)
{
L.ArgumentError(2, "invalid option");
return 0;
}
if (stream.BaseStream.Position >= stream.BaseStream.Length)
{
L.PushNil();
return 0;
}
var reader = new StreamReader(stream.BaseStream);
switch (option[0])
{
case 'a':
L.PushString(reader.ReadToEnd());
break;
case 'l':
L.PushString(reader.ReadLine());
break;
case 'n':
L.Error("Not yet implemented!");
break;
default:
L.ArgumentError(2, "invalid option");
break;
}
}
else
{
L.ArgumentError(2, "number or string expected");
return 0;
}
return 1;
}
private static int L_ReadAll(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position >= stream.BaseStream.Length)
{
L.PushNil();
return 0;
}
var chars = stream.ReadChars((int)stream.BaseStream.Length);
var buffer = Encoding.ASCII.GetBytes(chars);
L.PushBuffer(buffer);
return 1;
}
private static int L_Seek(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
var whence = L.CheckOption(2, "cur", new[]
{
"set", // begin 0
"cur", // current 1
"end", // end 2
null,
});
var offset = L.OptInteger(3, 0);
var newPosition = stream.BaseStream.Seek(offset, (SeekOrigin)whence);
L.PushInteger(newPosition);
return 1;
}
private static int L_ReadByte(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var count = (int)L.OptNumber(2, 1);
L.ArgumentCheck(count >= 1, 2, "count must be a positive integer");
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position >= stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
if (count == 1)
{
var b = stream.ReadByte();
L.PushInteger(b);
return 1;
}
else
{
var bs = stream.ReadBytes(count);
foreach (var b in bs)
{
L.PushInteger(b);
}
return bs.Length;
}
}
private static int L_ReadShort(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position >= stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadInt16();
L.PushInteger(b);
return 1;
}
private static int L_ReadInt(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadInt32();
L.PushInteger(b);
return 1;
}
private static int L_ReadLong(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadInt64();
L.PushInteger(b);
return 1;
}
private static int L_ReadSByte(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadSByte();
L.PushInteger(b);
return 1;
}
private static int L_ReadUShort(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadUInt16();
L.PushInteger(b);
return 1;
}
private static int L_ReadUInt(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadUInt32();
L.PushInteger(b);
return 1;
}
private static int L_ReadULong(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadUInt64();
L.PushInteger((long)b);
return 1;
}
private static int L_ReadHalf(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadHalf();
L.PushNumber((double)b);
return 1;
}
private static int L_ReadFloat(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadSingle();
L.PushNumber((double)b);
return 1;
}
private static int L_ReadDouble(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadDouble();
L.PushNumber((double)b);
return 1;
}
private static int L_ReadChar(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
var count = (int)L.OptNumber(2, 1);
L.ArgumentCheck(count >= 1, 2, "count must be a positive integer");
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position >= stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
if (count == 1)
{
var b = stream.ReadChar();
L.PushString(b.ToString());
return 1;
}
else
{
var bs = stream.ReadChars(count);
foreach (var b in bs)
{
L.PushString(b.ToString());
}
return bs.Length;
}
}
private static int L_ReadString(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
var count = (int)L.OptNumber(2, 1);
L.ArgumentCheck(count >= 1, 2, "count must be a positive integer");
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position >= stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
if (count == 1)
{
var b = stream.ReadChar();
L.PushString(b.ToString());
return 1;
}
else
{
var bs = stream.ReadBytes(count);
L.PushBuffer(bs);
return 1;
}
}
private static int L_ReadBoolean(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
if (stream.BaseStream.Position > stream.BaseStream.Length)
{
L.PushNil();
return 1;
}
var b = stream.ReadBoolean();
L.PushBoolean(b);
return 1;
}
private static int L_Close(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryReader>(1, ObjectType, true);
if (stream is null)
return 0;
stream.Dispose();
return 0;
}
}

View file

@ -1,395 +0,0 @@
using KeraLua;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Capy64.Runtime.Objects.Handlers;
public class BinaryWriteHandle
{
public const string ObjectType = "BinaryWriteHandle";
private static readonly Dictionary<string, LuaFunction> functions = new()
{
["seek"] = L_Seek,
["writeByte"] = L_WriteByte,
["writeShort"] = L_WriteShort,
["writeInt"] = L_WriteInt,
["writeLong"] = L_WriteLong,
["writeSByte"] = L_WriteSByte,
["writeUShort"] = L_WriteUShort,
["writeUInt"] = L_WriteUInt,
["writeULong"] = L_WriteULong,
["writeHalf"] = L_WriteHalf,
["writeFloat"] = L_WriteFloat,
["writeDouble"] = L_WriteDouble,
["writeChar"] = L_WriteChar,
["writeString"] = L_WriteString,
["writeBoolean"] = L_WriteBoolean,
["flush"] = L_Flush,
["close"] = L_Close,
};
public static void Push(Lua L, BinaryWriter stream)
{
L.PushObject(stream);
if (L.NewMetaTable(ObjectType))
{
L.PushString("__index");
L.NewTable();
foreach (var pair in functions)
{
L.PushString(pair.Key);
L.PushCFunction(pair.Value);
L.SetTable(-3);
}
L.SetTable(-3);
L.PushString("__close");
L.PushCFunction(L_Close);
L.SetTable(-3);
L.PushString("__gc");
L.PushCFunction(L_Close);
L.SetTable(-3);
}
L.SetMetaTable(-2);
}
private static int L_Seek(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
var whence = L.CheckOption(2, "cur", new[]
{
"set", // begin 0
"cur", // current 1
"end", // end 2
null,
});
var offset = L.OptInteger(3, 0);
var newPosition = stream.BaseStream.Seek(offset, (SeekOrigin)whence);
L.PushInteger(newPosition);
return 1;
}
private static int L_WriteByte(IntPtr state)
{
var L = Lua.FromIntPtr(state);
byte[] buffer;
if (L.IsInteger(2))
{
buffer = new[]
{
(byte)L.ToInteger(2),
};
}
else if (L.IsTable(2))
{
var len = L.RawLen(2);
buffer = new byte[len];
for (int i = 1; i <= len; i++)
{
L.PushInteger(i);
L.GetTable(2);
var b = L.CheckInteger(-1);
L.Pop(1);
buffer[i - 1] = (byte)b;
}
}
else
{
L.ArgumentError(2, "integer or table expected, got " + L.Type(2));
return 0;
}
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write(buffer);
return 0;
}
private static int L_WriteShort(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckInteger(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write((short)b);
return 0;
}
private static int L_WriteInt(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckInteger(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write((int)b);
return 0;
}
private static int L_WriteLong(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckInteger(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write(b);
return 0;
}
private static int L_WriteSByte(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckInteger(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write((sbyte)b);
return 0;
}
private static int L_WriteUShort(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckInteger(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write((ushort)b);
return 0;
}
private static int L_WriteUInt(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckInteger(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write((uint)b);
return 0;
}
private static int L_WriteULong(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckInteger(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write((ulong)b);
return 0;
}
private static int L_WriteHalf(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckNumber(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write((Half)b);
return 0;
}
private static int L_WriteFloat(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckNumber(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write((float)b);
return 0;
}
private static int L_WriteDouble(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckNumber(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write((double)b);
return 0;
}
private static int L_WriteChar(IntPtr state)
{
var L = Lua.FromIntPtr(state);
byte[] buffer;
if (L.IsString(2))
{
var str = L.ToString(2);
buffer = Encoding.ASCII.GetBytes(str);
}
else if (L.IsTable(2))
{
var len = L.RawLen(2);
var tmpBuffer = new List<byte>();
for (int i = 1; i <= len; i++)
{
L.PushInteger(i);
L.GetTable(2);
var b = L.CheckString(-1);
L.Pop(1);
var chunk = Encoding.ASCII.GetBytes(b);
foreach (var c in chunk)
{
tmpBuffer.Add(c);
}
}
buffer = tmpBuffer.ToArray();
}
else
{
L.ArgumentError(2, "integer or table expected, got " + L.Type(2));
return 0;
}
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write(buffer);
return 0;
}
private static int L_WriteString(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var b = L.CheckString(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write(b);
return 0;
}
private static int L_WriteBoolean(IntPtr state)
{
var L = Lua.FromIntPtr(state);
L.CheckType(2, LuaType.Boolean);
var b = L.ToBoolean(2);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Write(b);
return 0;
}
private static int L_Flush(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, false);
if (stream is null)
L.Error("handle is closed");
stream.Flush();
return 0;
}
private static int L_Close(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var stream = L.CheckObject<BinaryWriter>(1, ObjectType, true);
if (stream is null)
L.Error("handle is closed");
stream.Dispose();
return 0;
}
}

View file

@ -1,127 +0,0 @@
using KeraLua;
using System;
using System.Collections.Generic;
using System.IO;
namespace Capy64.Runtime.Objects.Handlers;
public class ReadHandle
{
public const string ObjectType = "ReadHandle";
private static readonly Dictionary<string, LuaFunction> functions = new()
{
["readAll"] = L_ReadAll,
["readLine"] = L_ReadLine,
["read"] = L_Read,
["close"] = L_Close,
};
public static void Push(Lua L, StreamReader stream)
{
L.PushObject(stream);
if (L.NewMetaTable(ObjectType))
{
L.PushString("__index");
L.NewTable();
foreach (var pair in functions)
{
L.PushString(pair.Key);
L.PushCFunction(pair.Value);
L.SetTable(-3);
}
L.SetTable(-3);
L.PushString("__close");
L.PushCFunction(L_Close);
L.SetTable(-3);
L.PushString("__gc");
L.PushCFunction(L_Close);
L.SetTable(-3);
}
L.SetMetaTable(-2);
}
private static int L_ReadAll(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var handle = L.CheckObject<StreamReader>(1, ObjectType, false);
if (handle is null)
L.Error("handle is closed");
if (handle.EndOfStream)
{
L.PushNil();
return 1;
}
var content = handle.ReadToEnd();
L.PushString(content);
return 1;
}
private static int L_ReadLine(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var handle = L.CheckObject<StreamReader>(1, ObjectType, false);
if (handle is null)
L.Error("handle is closed");
var line = handle.ReadLine();
if (line is null)
L.PushNil();
else
L.PushString(line);
return 1;
}
private static int L_Read(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var count = (int)L.OptNumber(2, 1);
L.ArgumentCheck(count >= 1, 2, "count must be a positive integer");
var handle = L.CheckObject<StreamReader>(1, ObjectType, false);
if (handle is null)
L.Error("handle is closed");
if (handle.EndOfStream)
{
L.PushNil();
return 1;
}
var chunk = new char[count];
handle.Read(chunk, 0, count);
L.PushString(new string(chunk));
return 1;
}
private static int L_Close(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var handle = L.CheckObject<StreamReader>(1, ObjectType, true);
if (handle is null)
return 0;
handle.Dispose();
return 0;
}
}

View file

@ -1,106 +0,0 @@
using KeraLua;
using System;
using System.Collections.Generic;
using System.IO;
namespace Capy64.Runtime.Objects.Handlers;
public class WriteHandle
{
public const string ObjectType = "WriteHandle";
private static readonly Dictionary<string, LuaFunction> functions = new()
{
["write"] = L_Write,
["writeLine"] = L_WriteLine,
["flush"] = L_Flush,
["close"] = L_Close,
};
public static void Push(Lua L, StreamWriter stream)
{
L.PushObject(stream);
if (L.NewMetaTable(ObjectType))
{
L.PushString("__index");
L.NewTable();
foreach (var pair in functions)
{
L.PushString(pair.Key);
L.PushCFunction(pair.Value);
L.SetTable(-3);
}
L.SetTable(-3);
L.PushString("__close");
L.PushCFunction(L_Close);
L.SetTable(-3);
L.PushString("__gc");
L.PushCFunction(L_Close);
L.SetTable(-3);
}
L.SetMetaTable(-2);
}
private static int L_Write(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var handle = L.CheckObject<StreamWriter>(1, ObjectType, false);
var content = L.CheckString(2);
if (handle is null)
L.Error("handle is closed");
handle.Write(content);
return 0;
}
private static int L_WriteLine(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var handle = L.CheckObject<StreamWriter>(1, ObjectType, false);
var content = L.CheckString(2);
if (handle is null)
L.Error("handle is closed");
handle.WriteLine(content);
return 0;
}
private static int L_Flush(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var handle = L.CheckObject<StreamWriter>(1, ObjectType, false);
if (handle is null)
L.Error("handle is closed");
handle.Flush();
return 0;
}
private static int L_Close(IntPtr state)
{
var L = Lua.FromIntPtr(state);
var handle = L.CheckObject<StreamWriter>(1, ObjectType, true);
if (handle is null)
return 0;
handle.Dispose();
return 0;
}
}

View file

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Net.WebSockets;
using System.Threading;
namespace Capy64.Runtime.Objects.Handlers;
namespace Capy64.Runtime.Objects;
public class WebSocketClient
{

View file

@ -84,8 +84,6 @@ internal class RuntimeManager : IPlugin
private void InitBIOS()
{
InstallOS(false);
_game.Discord.SetPresence("Booting up...");
luaState = new LuaState();
@ -104,8 +102,11 @@ internal class RuntimeManager : IPlugin
luaState.Thread.PushCFunction(L_OpenDataFolder);
luaState.Thread.SetGlobal("openDataFolder");
luaState.Thread.PushCFunction(L_InstallOS);
luaState.Thread.SetGlobal("installOS");
luaState.Thread.PushCFunction(L_ShouldInstallOS);
luaState.Thread.SetGlobal("shouldInstallOS");
luaState.Thread.PushCFunction(L_FlagInstalled);
luaState.Thread.SetGlobal("flagInstalled");
luaState.Thread.PushCFunction(L_Exit);
luaState.Thread.SetGlobal("exit");
@ -153,16 +154,6 @@ internal class RuntimeManager : IPlugin
_game.Exit();
}
public static void InstallOS(bool force = false)
{
var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed");
if (!File.Exists(installedFilePath) || force)
{
FileSystem.CopyDirectory("Assets/Lua", FileSystem.DataPath, true, true);
File.Create(installedFilePath).Dispose();
}
}
private static int L_OpenDataFolder(IntPtr state)
{
var path = FileSystem.DataPath;
@ -179,9 +170,22 @@ internal class RuntimeManager : IPlugin
return 0;
}
private static int L_InstallOS(IntPtr state)
private static int L_ShouldInstallOS(IntPtr state)
{
InstallOS(true);
var L = Lua.FromIntPtr(state);
var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed");
L.PushBoolean(!File.Exists(installedFilePath));
return 1;
}
private static int L_FlagInstalled(IntPtr state)
{
var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed");
if (!File.Exists(installedFilePath))
File.Create(installedFilePath).Dispose();
return 0;
}