mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-12-16 19:15:45 +00:00
Compare commits
No commits in common. "main" and "v1.0.0-beta" have entirely different histories.
main
...
v1.0.0-bet
96 changed files with 737 additions and 6114 deletions
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
run: dotnet test --no-build --verbosity normal
|
run: dotnet test --no-build --verbosity normal
|
||||||
|
|
||||||
- name: Publish for Windows x64
|
- name: Publish for Windows x64
|
||||||
run: dotnet publish Capy64/Capy64.csproj -c Release -a x64 --os win -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:TieredCompilation=false -o capy64-windows-x64 --self-contained
|
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os win -p:PublishSingleFile=true -o capy64-windows-x64
|
||||||
|
|
||||||
- name: Upload Windows x64 artifact
|
- name: Upload Windows x64 artifact
|
||||||
uses: actions/upload-artifact@v3.1.2
|
uses: actions/upload-artifact@v3.1.2
|
||||||
|
|
@ -64,7 +64,7 @@ jobs:
|
||||||
run: dotnet test --no-build --verbosity normal
|
run: dotnet test --no-build --verbosity normal
|
||||||
|
|
||||||
- name: Publish for Linux x64
|
- name: Publish for Linux x64
|
||||||
run: dotnet publish Capy64/Capy64.csproj -c Release -a x64 --os linux -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:TieredCompilation=false -o capy64-linux-x64 --self-contained
|
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os linux -p:PublishSingleFile=true -o capy64-linux-x64
|
||||||
|
|
||||||
- name: Upload Linux x64 artifact
|
- name: Upload Linux x64 artifact
|
||||||
uses: actions/upload-artifact@v3.1.2
|
uses: actions/upload-artifact@v3.1.2
|
||||||
|
|
|
||||||
36
Capy64/.config/dotnet-tools.json
Normal file
36
Capy64/.config/dotnet-tools.json
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"dotnet-mgcb": {
|
||||||
|
"version": "3.8.1.303",
|
||||||
|
"commands": [
|
||||||
|
"mgcb"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dotnet-mgcb-editor": {
|
||||||
|
"version": "3.8.1.303",
|
||||||
|
"commands": [
|
||||||
|
"mgcb-editor"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dotnet-mgcb-editor-linux": {
|
||||||
|
"version": "3.8.1.303",
|
||||||
|
"commands": [
|
||||||
|
"mgcb-editor-linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dotnet-mgcb-editor-windows": {
|
||||||
|
"version": "3.8.1.303",
|
||||||
|
"commands": [
|
||||||
|
"mgcb-editor-windows"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dotnet-mgcb-editor-mac": {
|
||||||
|
"version": "3.8.1.303",
|
||||||
|
"commands": [
|
||||||
|
"mgcb-editor-mac"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,10 +14,13 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Capy64.API;
|
namespace Capy64.API;
|
||||||
|
|
||||||
public interface IComponent
|
public interface IComponent
|
||||||
{
|
{
|
||||||
|
void ConfigureServices(IServiceCollection services) { }
|
||||||
void LuaInit(Lua L) { }
|
void LuaInit(Lua L) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
alias ll "ls -al"
|
|
||||||
alias la "ls -a"
|
|
||||||
alias rmdir "rm -r"
|
|
||||||
alias reboot "shutdown -r"
|
|
||||||
|
|
||||||
# Comment or remove the line below to disable the MOTD
|
|
||||||
motd
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
local version = "0.1.1"
|
|
||||||
local systemDirectory = "/sys"
|
|
||||||
|
|
||||||
print("Starting CapyOS")
|
|
||||||
|
|
||||||
local term = require("term")
|
|
||||||
local fs = require("fs")
|
|
||||||
local machine = require("machine")
|
|
||||||
|
|
||||||
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.setPos(1, 1)
|
|
||||||
term.write(machine.version())
|
|
||||||
term.setPos(1, 3)
|
|
||||||
|
|
||||||
local files = fs.list(fs.combine(systemDirectory, "boot/autorun"))
|
|
||||||
for i = 1, #files do
|
|
||||||
local func, err = loadfile(fs.combine(systemDirectory, "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
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
|
|
||||||
|
|
||||||
if options.l or options.list then
|
|
||||||
for alias, value in pairs(shell.aliases) do
|
|
||||||
print(string.format("%s = \"%s\"", alias, value))
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local alias = args[1]
|
|
||||||
|
|
||||||
if not alias or options.h or options.help then
|
|
||||||
print("Usage: alias [option...] <alias> [command]")
|
|
||||||
print("Options:")
|
|
||||||
print(" -l --list: List aliases")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local command = table.pack(select(2, ...))
|
|
||||||
if #command == 0 then
|
|
||||||
shell.aliases[alias] = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
shell.aliases[alias] = table.concat(command, " ")
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
local scheduler = require("scheduler")
|
|
||||||
scheduler.spawn(function()
|
|
||||||
shell.run(arg.string)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
|
|
||||||
local args = {...}
|
|
||||||
|
|
||||||
local path = shell.resolve(args[1])
|
|
||||||
|
|
||||||
local f<close> = fs.open(path, "r")
|
|
||||||
print(f:read("a"))
|
|
||||||
f:close()
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
local term = require("term")
|
|
||||||
local colors = require("colors")
|
|
||||||
term.setBackground(colors.black)
|
|
||||||
term.clear()
|
|
||||||
term.setPos(1, 1)
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
print(table.concat(args, " "))
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
shell.exit()
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
local gpu = require("gpu")
|
|
||||||
local event = require("event")
|
|
||||||
local donuts = {}
|
|
||||||
local limit = 100
|
|
||||||
|
|
||||||
local w, h = gpu.getSize()
|
|
||||||
local function insert()
|
|
||||||
local donut = {
|
|
||||||
x = math.random(-20, w + 20),
|
|
||||||
y = math.random(-20, h + 20),
|
|
||||||
d = math.random() * math.pi*2,
|
|
||||||
dir = math.random(0, 1),
|
|
||||||
c = math.random(0xffffff),
|
|
||||||
life = math.random(100, 1000),
|
|
||||||
}
|
|
||||||
table.insert(donuts, donut)
|
|
||||||
end
|
|
||||||
|
|
||||||
while true do
|
|
||||||
if #donuts < limit then
|
|
||||||
insert()
|
|
||||||
end
|
|
||||||
gpu.clear(0)
|
|
||||||
for k, donut in ipairs(donuts) do
|
|
||||||
if donut.life <= 0 then
|
|
||||||
table.remove(donuts, k)
|
|
||||||
end
|
|
||||||
local doReverse = math.random(0, 1000) > 950
|
|
||||||
donut.x = donut.x + math.cos(donut.d) * 4
|
|
||||||
donut.y = donut.y + math.sin(donut.d) * 4
|
|
||||||
donut.d = donut.d + (donut.dir == 1 and 0.05 or -0.05)
|
|
||||||
gpu.drawCircle(donut.x, donut.y, 20, donut.c, 10)
|
|
||||||
if doReverse then
|
|
||||||
donut.dir = donut.dir == 1 and 0 or 1
|
|
||||||
end
|
|
||||||
donut.life = donut.life - 1
|
|
||||||
end
|
|
||||||
event.push("donuts")
|
|
||||||
event.pull("donuts")
|
|
||||||
end
|
|
||||||
|
|
@ -1,98 +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 size = w * h
|
|
||||||
local canvas = { string.unpack(("B"):rep(size), ("\0"):rep(size)) }
|
|
||||||
canvas[#canvas] = nil
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
canvas[y * w + x] = colorUnit * (iterations - i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local buffer <close> = gpu.bufferFrom(canvas, w, h)
|
|
||||||
gpu.setBuffer(buffer)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- no idea why it's needed
|
|
||||||
timer.sleep(0)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -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.delay(0):await()
|
|
||||||
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.delay(0.1):await()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
parallel.waitForAny(draw, melt, random)
|
|
||||||
|
|
@ -1,209 +0,0 @@
|
||||||
local event = require("event")
|
|
||||||
local gpu = require("gpu")
|
|
||||||
local colors = require("colors")
|
|
||||||
local term = require("term")
|
|
||||||
local timer = require("timer")
|
|
||||||
|
|
||||||
local w, h = gpu.getSize()
|
|
||||||
local tw, th = term.getSize()
|
|
||||||
|
|
||||||
local selectedColor = 1
|
|
||||||
local thickness = 4
|
|
||||||
|
|
||||||
local canvasW, canvasH = term.toRealPos(tw - 1, th + 1)
|
|
||||||
canvasW = canvasW - 3
|
|
||||||
local size = canvasW * canvasH
|
|
||||||
local canvas = {string.unpack(("B"):rep(size), ("\0"):rep(size))}
|
|
||||||
canvas[#canvas] = nil
|
|
||||||
|
|
||||||
local function drawCircle(buffer, x, y, radius, color)
|
|
||||||
radius = math.max(0, radius)
|
|
||||||
if radius == 0 then
|
|
||||||
buffer[x + buffer.width * y] = color
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local width = buffer.width
|
|
||||||
local height = buffer.height
|
|
||||||
|
|
||||||
local index = function(x, y)
|
|
||||||
return y * width + x
|
|
||||||
end
|
|
||||||
|
|
||||||
local isValid = function(x, y)
|
|
||||||
return x >= 0 and x < width and y >= 0 and y < height
|
|
||||||
end
|
|
||||||
|
|
||||||
local setPixel = function(x, y, color)
|
|
||||||
if isValid(x, y) then
|
|
||||||
buffer[index(x, y)] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local drawFilledCirclePoints = function(cx, cy, x, y)
|
|
||||||
for dx = -x, x do
|
|
||||||
for dy = -y, y do
|
|
||||||
setPixel(cx + dx, cy + dy, color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local drawCircleBresenham = function(cx, cy, radius)
|
|
||||||
local x = 0
|
|
||||||
local y = radius
|
|
||||||
local d = 3 - 2 * radius
|
|
||||||
drawFilledCirclePoints(cx, cy, x, y)
|
|
||||||
while y >= x do
|
|
||||||
x = x + 1
|
|
||||||
if d > 0 then
|
|
||||||
y = y - 1
|
|
||||||
d = d + 4 * (x - y) + 10
|
|
||||||
else
|
|
||||||
d = d + 4 * x + 6
|
|
||||||
end
|
|
||||||
drawFilledCirclePoints(cx, cy, x, y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
drawCircleBresenham(x, y, radius)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drawLine(buffer, x0, y0, x1, y1, color, thickness)
|
|
||||||
local width = canvasW
|
|
||||||
local height = canvasH
|
|
||||||
|
|
||||||
local index = function(x, y)
|
|
||||||
return y * width + x
|
|
||||||
end
|
|
||||||
|
|
||||||
local isValid = function(x, y)
|
|
||||||
return x >= 0 and x < width and y >= 0 and y < height
|
|
||||||
end
|
|
||||||
|
|
||||||
local setPixel = function(x, y)
|
|
||||||
if isValid(x, y) then
|
|
||||||
buffer[index(x, y)] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local drawLineBresenham = function()
|
|
||||||
local i = 0
|
|
||||||
local dx = math.abs(x1 - x0)
|
|
||||||
local dy = math.abs(y1 - y0)
|
|
||||||
local sx = x0 < x1 and 1 or -1
|
|
||||||
local sy = y0 < y1 and 1 or -1
|
|
||||||
local err = dx - dy
|
|
||||||
|
|
||||||
local majorAxis = dx > dy
|
|
||||||
|
|
||||||
while x0 ~= x1 or y0 ~= y1 do
|
|
||||||
for i = 0, thickness - 1 do
|
|
||||||
if majorAxis then
|
|
||||||
setPixel(x0, y0 + i)
|
|
||||||
else
|
|
||||||
setPixel(x0 + i, y0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local err2 = 2 * err
|
|
||||||
if err2 > -dy then
|
|
||||||
err = err - dy
|
|
||||||
x0 = x0 + sx
|
|
||||||
end
|
|
||||||
if err2 < dx then
|
|
||||||
err = err + dx
|
|
||||||
y0 = y0 + sy
|
|
||||||
end
|
|
||||||
|
|
||||||
if i % 1024 == 0 then
|
|
||||||
--event.push("paint")
|
|
||||||
--event.pull("paint")
|
|
||||||
--timer.sleep(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
drawLineBresenham()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drawUI()
|
|
||||||
term.setBackground(0)
|
|
||||||
term.clear()
|
|
||||||
for y = 1, 16 do
|
|
||||||
term.setPos(tw - 1, y)
|
|
||||||
term.setBackground(0)
|
|
||||||
term.setForeground(colors[y])
|
|
||||||
if selectedColor == y then
|
|
||||||
term.setBackground(colors[y])
|
|
||||||
term.write(" ")
|
|
||||||
else
|
|
||||||
term.write("##")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
term.setPos(tw - 1, 17)
|
|
||||||
|
|
||||||
if selectedColor == 0 then
|
|
||||||
term.setBackground(colors.white)
|
|
||||||
term.setForeground(0)
|
|
||||||
else
|
|
||||||
term.setBackground(0)
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
end
|
|
||||||
term.write("XX")
|
|
||||||
|
|
||||||
term.setPos(tw - 1, 18)
|
|
||||||
term.setBackground(colors.black)
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
term.write(thickness)
|
|
||||||
|
|
||||||
gpu.drawLine(canvasW + 1, 0, canvasW, canvasH, colors.gray, 2)
|
|
||||||
|
|
||||||
local b<close> = gpu.bufferFrom(canvas, canvasW, canvasH)
|
|
||||||
gpu.drawBuffer(b, 0, 0, {
|
|
||||||
source = {
|
|
||||||
0, 0, canvasW, canvasH
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function contains(arr, val)
|
|
||||||
for i, v in ipairs(arr) do
|
|
||||||
if v == val then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local oldX, oldY
|
|
||||||
while true do
|
|
||||||
drawUI()
|
|
||||||
|
|
||||||
local ev, b, x, y = event.pull("mouse_down", "mouse_up", "mouse_move", "mouse_scroll")
|
|
||||||
local tx, ty = term.fromRealPos(x, y)
|
|
||||||
if ev == "mouse_up" then
|
|
||||||
if x >= canvasW then
|
|
||||||
if ty <= 16 then
|
|
||||||
selectedColor = ty
|
|
||||||
elseif ty == 17 then
|
|
||||||
selectedColor = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
oldX, oldY = nil, nil
|
|
||||||
elseif ev == "mouse_down" or (ev == "mouse_move" and contains(b, 1)) then
|
|
||||||
if x < canvasW and y < canvasH then
|
|
||||||
--canvas[x + y * canvasW] = colors[selectedColor] or 0
|
|
||||||
--drawCircle(canvas, x, y, thickness - 2, colors[selectedColor])
|
|
||||||
|
|
||||||
drawLine(canvas, x, y, oldX or x, oldY or y, colors[selectedColor] or 0, thickness)
|
|
||||||
--gpu.drawLine(x, y, oldX or x, oldY or y, colors[selectedColor] or 0)
|
|
||||||
--canvas = gpu.getBuffer()
|
|
||||||
|
|
||||||
oldX, oldY = x, y
|
|
||||||
end
|
|
||||||
elseif ev == "mouse_scroll" then
|
|
||||||
local x, y, b = b, x, y
|
|
||||||
local tx, ty = term.fromRealPos(x, y)
|
|
||||||
if x >= canvasW and ty == 18 then
|
|
||||||
thickness = math.min(99, math.max(0, thickness - b))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,23 +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 args = {...}
|
|
||||||
local text = "Hello, World!"
|
|
||||||
if #args > 0 then
|
|
||||||
text = table.concat(args, " ")
|
|
||||||
end
|
|
||||||
|
|
||||||
local color = colors[math.random(1, #colors)]
|
|
||||||
|
|
||||||
term.setForeground(color)
|
|
||||||
slowPrint(text, 0.05)
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local helpPath = "/sys/share/help"
|
|
||||||
|
|
||||||
local topicName = arg[1] or "index"
|
|
||||||
|
|
||||||
if not fs.exists(fs.combine(helpPath, topicName)) then
|
|
||||||
print(string.format("Topic \"%s\" not found.", topicName))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
shell.run("/sys/bin/less.lua", fs.combine(helpPath, topicName))
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
local term = require("term")
|
|
||||||
local keys = require("keys")
|
|
||||||
local event = require("event")
|
|
||||||
local fs = require("fs")
|
|
||||||
local timer = require("timer")
|
|
||||||
local colors = require("colors")
|
|
||||||
|
|
||||||
local filename = shell.resolve(arg[1])
|
|
||||||
|
|
||||||
local f<close> = fs.open(filename, "r")
|
|
||||||
local lines = {}
|
|
||||||
local lineMax = 0
|
|
||||||
for line in f:lines() do
|
|
||||||
table.insert(lines, line)
|
|
||||||
lineMax = math.max(lineMax, #line)
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
|
|
||||||
|
|
||||||
local width, height = term.getSize()
|
|
||||||
height = height - 1
|
|
||||||
local posx, posy = 0, 0
|
|
||||||
|
|
||||||
local function redraw()
|
|
||||||
term.clear()
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
for i = 1, height do
|
|
||||||
if i + posy > #lines then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
term.setPos(-posx + 1, i)
|
|
||||||
term.write(lines[i + posy])
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setForeground(colors.yellow)
|
|
||||||
term.setPos(1, height + 1)
|
|
||||||
term.write("Use arrow keys to move or press Q to exit.")
|
|
||||||
end
|
|
||||||
|
|
||||||
while true do
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
local _, key = event.pull("key_down")
|
|
||||||
|
|
||||||
if key == keys.enter or key == keys.down then
|
|
||||||
posy = posy + 1
|
|
||||||
elseif key == keys.up then
|
|
||||||
posy = posy - 1
|
|
||||||
elseif key == keys.right then
|
|
||||||
posx = posx + 1
|
|
||||||
elseif key == keys.left then
|
|
||||||
posx = posx - 1
|
|
||||||
elseif key == keys.q or key == keys.escape then
|
|
||||||
-- Clear event queue
|
|
||||||
timer.sleep(0)
|
|
||||||
term.clear()
|
|
||||||
term.setPos(1, 1)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if posy > #lines - height then
|
|
||||||
posy = #lines - height
|
|
||||||
end
|
|
||||||
|
|
||||||
if posy < 0 then
|
|
||||||
posy = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if posx + width > lineMax + 1 then
|
|
||||||
posx = lineMax - width + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if posx < 0 then
|
|
||||||
posx = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local term = require("term")
|
|
||||||
local colors = require("colors")
|
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local theme = {
|
|
||||||
directory = colors.lightBlue,
|
|
||||||
file = colors.white,
|
|
||||||
lua = colors.yellow,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function humanizeBytes(n)
|
|
||||||
local prefixes = {
|
|
||||||
[0] = "",
|
|
||||||
"k",
|
|
||||||
"M",
|
|
||||||
"G",
|
|
||||||
"T",
|
|
||||||
}
|
|
||||||
local block = 1024
|
|
||||||
local prefixIndex = 0
|
|
||||||
|
|
||||||
while n >= block do
|
|
||||||
n = n / 1024
|
|
||||||
prefixIndex = prefixIndex + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return string.format("%.0f%s", n, prefixes[prefixIndex])
|
|
||||||
end
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
|
|
||||||
if options.h or options.help then
|
|
||||||
print("Usage: ls [option...] [path]")
|
|
||||||
print("List files (current directory by default)")
|
|
||||||
print("Options:")
|
|
||||||
print(" -a: Include hidden files")
|
|
||||||
print(" -l: Use long listing format")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local path = shell.getDir()
|
|
||||||
|
|
||||||
if args[1] then
|
|
||||||
path = shell.resolve(args[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
if not fs.isDir(path) then
|
|
||||||
error("No such directory: " .. path, 0)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local entries = fs.list(path)
|
|
||||||
|
|
||||||
if options.l then
|
|
||||||
print(string.format("total %d", #entries))
|
|
||||||
end
|
|
||||||
local printed = 0
|
|
||||||
for i, entry in ipairs(entries) do
|
|
||||||
if entry:sub(1, 1) ~= "." or options.a then
|
|
||||||
printed = printed + 1
|
|
||||||
local attributes = fs.attributes(fs.combine(path, entry))
|
|
||||||
local size = humanizeBytes(attributes.size)
|
|
||||||
local date = os.date("%x %H:%m", attributes.modified // 1000)
|
|
||||||
|
|
||||||
local entryType
|
|
||||||
if attributes.isDirectory then
|
|
||||||
entryType = "directory"
|
|
||||||
else
|
|
||||||
entryType = "file"
|
|
||||||
if string.match(entry, "%.lua$") then
|
|
||||||
entryType = "lua"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if options.l then
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
term.write(string.format("%s %5s %s ", attributes.isDirectory and "d" or "-", size, date))
|
|
||||||
end
|
|
||||||
term.setForeground(theme[entryType])
|
|
||||||
io.write(entry)
|
|
||||||
|
|
||||||
io.write(options.l and "\n" or "\t")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not options.l and printed > 0 then
|
|
||||||
print()
|
|
||||||
end
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
local term = require("term")
|
|
||||||
local io = require("io")
|
|
||||||
local colors = require("colors")
|
|
||||||
local argparser = require("argparser")
|
|
||||||
local tableutils = require("tableutils")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
|
|
||||||
local function evaluate(str, env, chunkname)
|
|
||||||
chunkname = chunkname or "=lua"
|
|
||||||
local nForcePrint = 0
|
|
||||||
local func, e = load(str, chunkname, "t", env)
|
|
||||||
local func2 = load("return " .. str, chunkname, "t", env)
|
|
||||||
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(tableutils.pretty(value))
|
|
||||||
n = n + 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
io.stderr.print(tResults[2])
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
else
|
|
||||||
io.stderr.print(e)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function createEnvironment()
|
|
||||||
return setmetatable({}, { __index = _ENV })
|
|
||||||
end
|
|
||||||
|
|
||||||
local function loadPackages(env)
|
|
||||||
for k, v in pairs(package.loaded) do
|
|
||||||
env[k] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if options.e then
|
|
||||||
local env = createEnvironment()
|
|
||||||
loadPackages(env)
|
|
||||||
return evaluate(table.concat(args, " "), env)
|
|
||||||
end
|
|
||||||
|
|
||||||
if #args > 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 = createEnvironment()
|
|
||||||
tEnv.exit = setmetatable({}, {
|
|
||||||
__tostring = function() return "Call exit() to exit." end,
|
|
||||||
__call = function() bRunning = false end,
|
|
||||||
})
|
|
||||||
loadPackages(tEnv)
|
|
||||||
|
|
||||||
term.setForeground(colors.yellow)
|
|
||||||
print(_VERSION .. " interactive prompt")
|
|
||||||
print("Call exit() to exit.")
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
|
|
||||||
while bRunning do
|
|
||||||
term.setForeground(colors.yellow)
|
|
||||||
io.write("> ")
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
|
|
||||||
local s = io.read(nil, tCommandHistory)
|
|
||||||
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
|
||||||
table.insert(tCommandHistory, s)
|
|
||||||
end
|
|
||||||
|
|
||||||
evaluate(s, tEnv)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
|
|
||||||
local date = os.date("*t")
|
|
||||||
|
|
||||||
if date.month == 4 and date.day == 28 then
|
|
||||||
print("Ed Balls")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local motdList = {}
|
|
||||||
|
|
||||||
local f<close> = fs.open("/sys/share/motd.txt", "r")
|
|
||||||
for line in f:lines() do
|
|
||||||
table.insert(motdList, line)
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
|
|
||||||
local motdIndex = math.random(1, #motdList)
|
|
||||||
|
|
||||||
print(motdList[motdIndex])
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
|
|
||||||
if not args[1] or not args[2] or options.h or options.help then
|
|
||||||
print("Usage: mv [option...] <source> <target>")
|
|
||||||
print("Options:")
|
|
||||||
print(" -h --help: Display help")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local source = shell.resolve(args[1])
|
|
||||||
local destination = shell.resolve(args[2])
|
|
||||||
|
|
||||||
fs.move(source, destination)
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local programs = fs.list("/sys/bin", function(name, attr)
|
|
||||||
return not attr.isDirectory
|
|
||||||
end)
|
|
||||||
|
|
||||||
for i, v in ipairs(programs) do
|
|
||||||
programs[i] = string.gsub(v, "%.lua$", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
print(table.concat(programs, " "))
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
print(shell.getDir())
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
|
|
||||||
if not args[1] or options.h or options.help then
|
|
||||||
print("Usage: rm [option...] <path>")
|
|
||||||
print("Options:")
|
|
||||||
print(" -r --recursive: Delete non-empty directories")
|
|
||||||
print(" -h --help: Display help")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local file = shell.resolve(args[1])
|
|
||||||
|
|
||||||
fs.delete(file, options.recursive or options.r)
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
local term = require("term")
|
|
||||||
local colors = require("colors")
|
|
||||||
local fs = require("fs")
|
|
||||||
local machine = require("machine")
|
|
||||||
local argparser = require("argparser")
|
|
||||||
local scheduler = require("scheduler")
|
|
||||||
local createPackageEnvironment = require("shell.package")
|
|
||||||
|
|
||||||
local useScheduler = false
|
|
||||||
local exit = false
|
|
||||||
local parentShell = shell
|
|
||||||
local isStartupShell = parentShell == nil
|
|
||||||
local shell = {}
|
|
||||||
|
|
||||||
shell.path = parentShell and parentShell.path or "./?;./?.lua;/bin/?.lua;/sys/bin/?.lua"
|
|
||||||
shell.homePath = parentShell and parentShell.home or "/home"
|
|
||||||
shell.aliases = parentShell and parentShell.aliases or {}
|
|
||||||
|
|
||||||
local currentDir = parentShell and parentShell.getDir() or shell.homePath
|
|
||||||
|
|
||||||
local function buildEnvironment(command, filepath, args, argf)
|
|
||||||
local arg = { table.unpack(args, 2) }
|
|
||||||
arg[0] = command
|
|
||||||
arg.string = argf
|
|
||||||
|
|
||||||
local envPackage = createPackageEnvironment(filepath)
|
|
||||||
envPackage.loaded.scheduler = scheduler
|
|
||||||
|
|
||||||
return setmetatable({
|
|
||||||
shell = shell,
|
|
||||||
arg = arg,
|
|
||||||
scheduler = scheduler,
|
|
||||||
}, { __index = envPackage.loaded._G })
|
|
||||||
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 = argparser.tokenize(...)
|
|
||||||
local argf = table.concat({...}, " ")
|
|
||||||
local command = args[1]
|
|
||||||
|
|
||||||
argf = argf:sub(#command + 2)
|
|
||||||
|
|
||||||
local path = shell.resolveProgram(command)
|
|
||||||
|
|
||||||
if not path then
|
|
||||||
if shell.aliases[command] then
|
|
||||||
return shell.run(shell.aliases[command], select(2, table.unpack(args)))
|
|
||||||
else
|
|
||||||
io.stderr.print("Command not found: " .. command)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local env = buildEnvironment(command, path, args, argf)
|
|
||||||
|
|
||||||
local func, err = loadfile(path, "t", env)
|
|
||||||
|
|
||||||
if not func then
|
|
||||||
io.stderr.print(err)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, err
|
|
||||||
local function run()
|
|
||||||
ok, err = pcall(func, table.unpack(args, 2))
|
|
||||||
end
|
|
||||||
|
|
||||||
if useScheduler then
|
|
||||||
local programTask, yielded = scheduler.spawn(run)
|
|
||||||
|
|
||||||
if yielded then
|
|
||||||
coroutine.yield("scheduler_task_end")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
run()
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
term.setBackground(colors.black)
|
|
||||||
|
|
||||||
if isStartupShell then
|
|
||||||
if fs.exists(fs.combine(shell.homePath, ".shrc")) then
|
|
||||||
local f <close> = fs.open(fs.combine(shell.homePath, ".shrc"), "r")
|
|
||||||
for line in f:lines() do
|
|
||||||
if line:match("%S") and not line:match("^%s-#") then
|
|
||||||
shell.run(line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
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
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
local machine = require("machine")
|
|
||||||
local scheduler = require("scheduler")
|
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
|
|
||||||
if options.h or options.help then
|
|
||||||
print("Usage: shutdown [option...]")
|
|
||||||
print("Shutdown or restart Capy64.")
|
|
||||||
print("Options:")
|
|
||||||
print(" -s --shutdown: Shutdown and exit Capy64. (default)")
|
|
||||||
print(" -r --reboot: Restart Capy64.")
|
|
||||||
print(" -t --time: Time to wait in seconds. (\"now\" is 0 seconds, default)")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local time = 0
|
|
||||||
if options.t or options.time then
|
|
||||||
time = options.t or options.time
|
|
||||||
end
|
|
||||||
if time == "now" then
|
|
||||||
time = 0
|
|
||||||
else
|
|
||||||
time = tonumber(time)
|
|
||||||
if not time then
|
|
||||||
error("Invalid time option: " .. (options.t or options.time), 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
scheduler.ipc(1, "power", {
|
|
||||||
reboot = options.r or options.reboot,
|
|
||||||
time = time,
|
|
||||||
})
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
local machine = require("machine")
|
|
||||||
print(string.format("%s @ %s - %s", os.version(), machine.version(), _VERSION))
|
|
||||||
|
|
@ -1,36 +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
|
|
||||||
|
|
||||||
print("Connecting...")
|
|
||||||
|
|
||||||
local response, 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.content:read("a"))
|
|
||||||
file:close()
|
|
||||||
response.content:close()
|
|
||||||
|
|
||||||
print("Downloaded to " .. outputPath)
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
package.path = "/lib/?.lua;/lib/?/init.lua;/sys/lib/?.lua;/sys/lib/?/init.lua;" .. package.path
|
|
||||||
|
|
||||||
_G.io = require("io")
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local expect = require("expect").expect
|
|
||||||
|
|
||||||
local fsList = fs.list
|
|
||||||
|
|
||||||
function fs.list(path, filter)
|
|
||||||
expect(1, path, "string")
|
|
||||||
expect(2, filter, "nil", "function")
|
|
||||||
|
|
||||||
if not fs.isDir(path) then
|
|
||||||
error("directory not found", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local list = fsList(path)
|
|
||||||
if not filter then
|
|
||||||
return list
|
|
||||||
end
|
|
||||||
|
|
||||||
local filteredList = {}
|
|
||||||
for i = 1, #list do
|
|
||||||
local attributes = fs.attributes(fs.combine(path, list[i]))
|
|
||||||
if filter(list[i], attributes) then
|
|
||||||
table.insert(filteredList, list[i])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return filteredList
|
|
||||||
end
|
|
||||||
|
|
@ -1,77 +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 task<close> = http.requestAsync(url, body, headers, options)
|
|
||||||
return task:await()
|
|
||||||
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 handle then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
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 task<close> = http.websocketAsync(url, headers)
|
|
||||||
local client, err = task:await()
|
|
||||||
|
|
||||||
return buildWebsocketHandle(client), err
|
|
||||||
end
|
|
||||||
|
|
@ -1,13 +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")
|
|
||||||
|
|
||||||
local timerId = timer.start(n)
|
|
||||||
repeat
|
|
||||||
local _, par = event.pull("timer")
|
|
||||||
until par == timerId
|
|
||||||
end
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
local machine = require("machine")
|
|
||||||
local scheduler = require("scheduler")
|
|
||||||
|
|
||||||
local term = require("term")
|
|
||||||
local colors = require("colors")
|
|
||||||
term.setForeground(0x59c9ff)
|
|
||||||
term.setBackground(colors.black)
|
|
||||||
term.clear()
|
|
||||||
term.setPos(1, 1)
|
|
||||||
|
|
||||||
term.write(os.version())
|
|
||||||
term.setPos(1, 2)
|
|
||||||
|
|
||||||
local function spawnShell()
|
|
||||||
return scheduler.spawn(loadfile("/sys/bin/shell.lua"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function main()
|
|
||||||
local shellTask = spawnShell()
|
|
||||||
while true do
|
|
||||||
local ev = {coroutine.yield()}
|
|
||||||
if ev[1] == "ipc_message" then
|
|
||||||
local sender = ev[2]
|
|
||||||
local call = ev[3]
|
|
||||||
if call == "power" then
|
|
||||||
local options = ev[4]
|
|
||||||
--todo: handle time and cancels
|
|
||||||
if options.reboot then
|
|
||||||
machine.reboot()
|
|
||||||
else
|
|
||||||
machine.shutdown()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif ev[1] == "scheduler_task_end" then
|
|
||||||
if ev[2].pid == shellTask.pid then
|
|
||||||
if not ev[3] then
|
|
||||||
io.stderr.print(ev[4])
|
|
||||||
end
|
|
||||||
shellTask = spawnShell()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
scheduler.spawn(main)
|
|
||||||
|
|
||||||
scheduler.init()
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
require("machine").shutdown()
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
local argparser = {}
|
|
||||||
|
|
||||||
function argparser.tokenize(...)
|
|
||||||
local input = table.concat(table.pack(...), " ")
|
|
||||||
local tokens = {}
|
|
||||||
|
|
||||||
-- surely there must be a better way
|
|
||||||
local quoted = false
|
|
||||||
local escaped = false
|
|
||||||
local current = ""
|
|
||||||
for i = 1, #input do
|
|
||||||
local char = input:sub(i, i)
|
|
||||||
if escaped then
|
|
||||||
escaped = false
|
|
||||||
current = current .. char
|
|
||||||
else
|
|
||||||
if char == "\\" then
|
|
||||||
escaped = true
|
|
||||||
elseif char == "\"" then
|
|
||||||
if quoted then
|
|
||||||
-- close quote
|
|
||||||
table.insert(tokens, current)
|
|
||||||
current = ""
|
|
||||||
end
|
|
||||||
quoted = not quoted
|
|
||||||
elseif char == " " and not quoted then
|
|
||||||
if #current > 0 then
|
|
||||||
table.insert(tokens, current)
|
|
||||||
end
|
|
||||||
current = ""
|
|
||||||
else
|
|
||||||
current = current .. char
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if current ~= "" then
|
|
||||||
table.insert(tokens, current)
|
|
||||||
end
|
|
||||||
|
|
||||||
return tokens
|
|
||||||
end
|
|
||||||
|
|
||||||
function argparser.parse(...)
|
|
||||||
local tokens = { ... }
|
|
||||||
local args = {}
|
|
||||||
local options = {}
|
|
||||||
local ignoreOptions = false
|
|
||||||
|
|
||||||
for i = 1, #tokens do
|
|
||||||
local token = tokens[i]
|
|
||||||
if not ignoreOptions then
|
|
||||||
if token == "--" then
|
|
||||||
ignoreOptions = true
|
|
||||||
elseif token:sub(1, 2) == "--" then
|
|
||||||
local opt, value = token:match("%-%-(.+)=(.+)")
|
|
||||||
if not opt then
|
|
||||||
opt = token:sub(3)
|
|
||||||
if opt:sub(-1) == "=" then
|
|
||||||
-- next token is value
|
|
||||||
value = tokens[i + 1]
|
|
||||||
opt = opt:sub(1, -2)
|
|
||||||
options[opt] = value
|
|
||||||
i = i + 1
|
|
||||||
else
|
|
||||||
options[opt] = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
options[opt] = value
|
|
||||||
end
|
|
||||||
elseif token:sub(1, 1) == "-" then
|
|
||||||
local opts = token:sub(2)
|
|
||||||
for j = 1, #opts do
|
|
||||||
options[opts:sub(j, j)] = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if #token > 0 then
|
|
||||||
table.insert(args, token)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if #token > 0 then
|
|
||||||
table.insert(args, token)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return args, options
|
|
||||||
end
|
|
||||||
|
|
||||||
return argparser
|
|
||||||
|
|
@ -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;
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,616 +0,0 @@
|
||||||
local expect = require("expect").expect
|
|
||||||
local event = require("event")
|
|
||||||
local term = require("term")
|
|
||||||
local keys = require("keys")
|
|
||||||
local machine = require("machine")
|
|
||||||
|
|
||||||
local io = {}
|
|
||||||
|
|
||||||
function io.write(sText)
|
|
||||||
sText = tostring(sText)
|
|
||||||
|
|
||||||
local w, h = term.getSize()
|
|
||||||
local x, y = term.getPos()
|
|
||||||
|
|
||||||
local nLinesPrinted = 0
|
|
||||||
local function newLine()
|
|
||||||
if y + 1 <= h then
|
|
||||||
term.setPos(1, y + 1)
|
|
||||||
else
|
|
||||||
term.setPos(1, h)
|
|
||||||
term.scroll(1)
|
|
||||||
end
|
|
||||||
x, y = term.getPos()
|
|
||||||
nLinesPrinted = nLinesPrinted + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Print the line with proper word wrapping
|
|
||||||
sText = tostring(sText)
|
|
||||||
while #sText > 0 do
|
|
||||||
local whitespace = string.match(sText, "^[ \t]+")
|
|
||||||
if whitespace then
|
|
||||||
-- Print whitespace
|
|
||||||
term.write(whitespace)
|
|
||||||
x, y = term.getPos()
|
|
||||||
sText = string.sub(sText, #whitespace + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local newline = string.match(sText, "^\n")
|
|
||||||
if newline then
|
|
||||||
-- Print newlines
|
|
||||||
newLine()
|
|
||||||
sText = string.sub(sText, 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local text = string.match(sText, "^[^ \t\n]+")
|
|
||||||
if text then
|
|
||||||
sText = string.sub(sText, #text + 1)
|
|
||||||
if #text > w then
|
|
||||||
-- Print a multiline word
|
|
||||||
while #text > 0 do
|
|
||||||
if x > w then
|
|
||||||
newLine()
|
|
||||||
end
|
|
||||||
term.write(text)
|
|
||||||
text = string.sub(text, w - x + 2)
|
|
||||||
x, y = term.getPos()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Print a word normally
|
|
||||||
if x + #text - 1 > w then
|
|
||||||
newLine()
|
|
||||||
end
|
|
||||||
term.write(text)
|
|
||||||
x, y = term.getPos()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nLinesPrinted
|
|
||||||
end
|
|
||||||
|
|
||||||
function io.read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|
||||||
expect(1, _sReplaceChar, "string", "nil")
|
|
||||||
expect(2, _tHistory, "table", "nil")
|
|
||||||
expect(3, _fnComplete, "function", "nil")
|
|
||||||
expect(4, _sDefault, "string", "nil")
|
|
||||||
|
|
||||||
term.setBlink(true)
|
|
||||||
|
|
||||||
local sLine
|
|
||||||
if type(_sDefault) == "string" then
|
|
||||||
sLine = _sDefault
|
|
||||||
else
|
|
||||||
sLine = ""
|
|
||||||
end
|
|
||||||
local nHistoryPos
|
|
||||||
local nPos, nScroll = #sLine, 0
|
|
||||||
if _sReplaceChar then
|
|
||||||
_sReplaceChar = string.sub(_sReplaceChar, 1, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local tCompletions
|
|
||||||
local nCompletion
|
|
||||||
local function recomplete()
|
|
||||||
if _fnComplete and nPos == #sLine then
|
|
||||||
tCompletions = _fnComplete(sLine)
|
|
||||||
if tCompletions and #tCompletions > 0 then
|
|
||||||
nCompletion = 1
|
|
||||||
else
|
|
||||||
nCompletion = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
tCompletions = nil
|
|
||||||
nCompletion = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function uncomplete()
|
|
||||||
tCompletions = nil
|
|
||||||
nCompletion = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local w = term.getSize()
|
|
||||||
local sx = term.getPos()
|
|
||||||
|
|
||||||
local function redraw(_bClear)
|
|
||||||
local cursor_pos = nPos - nScroll
|
|
||||||
if sx + cursor_pos >= w then
|
|
||||||
-- We've moved beyond the RHS, ensure we're on the edge.
|
|
||||||
nScroll = sx + nPos - w
|
|
||||||
elseif cursor_pos < 0 then
|
|
||||||
-- We've moved beyond the LHS, ensure we're on the edge.
|
|
||||||
nScroll = nPos
|
|
||||||
end
|
|
||||||
|
|
||||||
local _, cy = term.getPos()
|
|
||||||
term.setPos(sx, cy)
|
|
||||||
local sReplace = _bClear and " " or _sReplaceChar
|
|
||||||
if sReplace then
|
|
||||||
term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
|
|
||||||
else
|
|
||||||
term.write(string.sub(sLine, nScroll + 1))
|
|
||||||
end
|
|
||||||
|
|
||||||
if nCompletion then
|
|
||||||
local sCompletion = tCompletions[nCompletion]
|
|
||||||
local oldText, oldBg
|
|
||||||
if not _bClear then
|
|
||||||
oldText = term.getTextColor()
|
|
||||||
oldBg = term.getBackgroundColor()
|
|
||||||
term.setTextColor(colors.white)
|
|
||||||
term.setBackgroundColor(colors.gray)
|
|
||||||
end
|
|
||||||
if sReplace then
|
|
||||||
term.write(string.rep(sReplace, #sCompletion))
|
|
||||||
else
|
|
||||||
term.write(sCompletion)
|
|
||||||
end
|
|
||||||
if not _bClear then
|
|
||||||
term.setTextColor(oldText)
|
|
||||||
term.setBackgroundColor(oldBg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setPos(sx + nPos - nScroll, cy)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function clear()
|
|
||||||
redraw(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
local function acceptCompletion()
|
|
||||||
if nCompletion then
|
|
||||||
-- Clear
|
|
||||||
clear()
|
|
||||||
|
|
||||||
-- Find the common prefix of all the other suggestions which start with the same letter as the current one
|
|
||||||
local sCompletion = tCompletions[nCompletion]
|
|
||||||
sLine = sLine .. sCompletion
|
|
||||||
nPos = #sLine
|
|
||||||
|
|
||||||
-- Redraw
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while true do
|
|
||||||
local sEvent, param, param1, param2 = event.pull()
|
|
||||||
if sEvent == "char" then
|
|
||||||
-- Typed key
|
|
||||||
clear()
|
|
||||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
|
||||||
nPos = nPos + 1
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
elseif sEvent == "paste" then
|
|
||||||
-- Pasted text
|
|
||||||
clear()
|
|
||||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
|
||||||
nPos = nPos + #param
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
elseif sEvent == "key_down" then
|
|
||||||
if param == keys.enter or param == keys.numPadEnter then
|
|
||||||
-- Enter/Numpad Enter
|
|
||||||
if nCompletion then
|
|
||||||
clear()
|
|
||||||
uncomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
break
|
|
||||||
|
|
||||||
elseif param == keys.left then
|
|
||||||
-- Left
|
|
||||||
if nPos > 0 then
|
|
||||||
clear()
|
|
||||||
nPos = nPos - 1
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.right then
|
|
||||||
-- Right
|
|
||||||
if nPos < #sLine then
|
|
||||||
-- Move right
|
|
||||||
clear()
|
|
||||||
nPos = nPos + 1
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
else
|
|
||||||
-- Accept autocomplete
|
|
||||||
acceptCompletion()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.up or param == keys.down then
|
|
||||||
-- Up or down
|
|
||||||
if nCompletion then
|
|
||||||
-- Cycle completions
|
|
||||||
clear()
|
|
||||||
if param == keys.up then
|
|
||||||
nCompletion = nCompletion - 1
|
|
||||||
if nCompletion < 1 then
|
|
||||||
nCompletion = #tCompletions
|
|
||||||
end
|
|
||||||
elseif param == keys.down then
|
|
||||||
nCompletion = nCompletion + 1
|
|
||||||
if nCompletion > #tCompletions then
|
|
||||||
nCompletion = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
elseif _tHistory then
|
|
||||||
-- Cycle history
|
|
||||||
clear()
|
|
||||||
if param == keys.up then
|
|
||||||
-- Up
|
|
||||||
if nHistoryPos == nil then
|
|
||||||
if #_tHistory > 0 then
|
|
||||||
nHistoryPos = #_tHistory
|
|
||||||
end
|
|
||||||
elseif nHistoryPos > 1 then
|
|
||||||
nHistoryPos = nHistoryPos - 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Down
|
|
||||||
if nHistoryPos == #_tHistory then
|
|
||||||
nHistoryPos = nil
|
|
||||||
elseif nHistoryPos ~= nil then
|
|
||||||
nHistoryPos = nHistoryPos + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if nHistoryPos then
|
|
||||||
sLine = _tHistory[nHistoryPos]
|
|
||||||
nPos, nScroll = #sLine, 0
|
|
||||||
else
|
|
||||||
sLine = ""
|
|
||||||
nPos, nScroll = 0, 0
|
|
||||||
end
|
|
||||||
uncomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.back then
|
|
||||||
-- Backspace
|
|
||||||
if nPos > 0 then
|
|
||||||
clear()
|
|
||||||
sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
|
|
||||||
nPos = nPos - 1
|
|
||||||
if nScroll > 0 then nScroll = nScroll - 1 end
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.home then
|
|
||||||
-- Home
|
|
||||||
if nPos > 0 then
|
|
||||||
clear()
|
|
||||||
nPos = 0
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.delete then
|
|
||||||
-- Delete
|
|
||||||
if nPos < #sLine then
|
|
||||||
clear()
|
|
||||||
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys["end"] then
|
|
||||||
-- End
|
|
||||||
if nPos < #sLine then
|
|
||||||
clear()
|
|
||||||
nPos = #sLine
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.tab then
|
|
||||||
-- Tab (accept autocomplete)
|
|
||||||
acceptCompletion()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif sEvent == "mouse_down" or sEvent == "mouse_drag" and param == 1 then
|
|
||||||
local _, cy = term.getPos()
|
|
||||||
if param1 >= sx and param1 <= w and param2 == cy then
|
|
||||||
-- Ensure we don't scroll beyond the current line
|
|
||||||
nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif sEvent == "term_resize" then
|
|
||||||
-- Terminal resized
|
|
||||||
w = term.getSize()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local _, cy = term.getPos()
|
|
||||||
term.setBlink(false)
|
|
||||||
term.setPos(w + 1, cy)
|
|
||||||
print()
|
|
||||||
|
|
||||||
return sLine
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--[[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
|
|
||||||
io.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, mods = 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 == "key_down" then
|
|
||||||
if par1 == keys.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 par1 == keys.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 par1 == keys.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 par1 == keys.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 par1 == keys.left then
|
|
||||||
if cursor_pos < #buffer then
|
|
||||||
clearCompletion()
|
|
||||||
cursor_pos = cursor_pos + 1
|
|
||||||
end
|
|
||||||
elseif par1 == keys.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 par1 == keys.tab then
|
|
||||||
if comp_id > 0 then
|
|
||||||
dirty = true
|
|
||||||
buffer = buffer .. completions[comp_id]
|
|
||||||
end
|
|
||||||
elseif par1 == keys.home then
|
|
||||||
cursor_pos = #buffer
|
|
||||||
elseif par1 == keys["end"] then
|
|
||||||
cursor_pos = 0
|
|
||||||
elseif par1 == keys.enter then
|
|
||||||
clearCompletion()
|
|
||||||
print()
|
|
||||||
break
|
|
||||||
elseif mods & keys.mods.ctrl ~= 0 then
|
|
||||||
if par1 == keys.v then
|
|
||||||
dirty = true
|
|
||||||
clearCompletion()
|
|
||||||
local text = machine.getClipboard()
|
|
||||||
if text then
|
|
||||||
if cursor_pos == 0 then
|
|
||||||
buffer = buffer .. text
|
|
||||||
elseif cursor_pos == #buffer then
|
|
||||||
buffer = text .. buffer
|
|
||||||
else
|
|
||||||
buffer = buffer:sub(0, -cursor_pos - 1) .. text ..
|
|
||||||
buffer:sub(-cursor_pos + (#text - 1))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
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
|
|
||||||
|
|
@ -1,388 +0,0 @@
|
||||||
--
|
|
||||||
-- json.lua
|
|
||||||
--
|
|
||||||
-- Copyright (c) 2020 rxi
|
|
||||||
--
|
|
||||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
-- this software and associated documentation files (the "Software"), to deal in
|
|
||||||
-- the Software without restriction, including without limitation the rights to
|
|
||||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
-- so, subject to the following conditions:
|
|
||||||
--
|
|
||||||
-- The above copyright notice and this permission notice shall be included in all
|
|
||||||
-- copies or substantial portions of the Software.
|
|
||||||
--
|
|
||||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
-- SOFTWARE.
|
|
||||||
--
|
|
||||||
|
|
||||||
local json = { _version = "0.1.2" }
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Encode
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local encode
|
|
||||||
|
|
||||||
local escape_char_map = {
|
|
||||||
[ "\\" ] = "\\",
|
|
||||||
[ "\"" ] = "\"",
|
|
||||||
[ "\b" ] = "b",
|
|
||||||
[ "\f" ] = "f",
|
|
||||||
[ "\n" ] = "n",
|
|
||||||
[ "\r" ] = "r",
|
|
||||||
[ "\t" ] = "t",
|
|
||||||
}
|
|
||||||
|
|
||||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
|
||||||
for k, v in pairs(escape_char_map) do
|
|
||||||
escape_char_map_inv[v] = k
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function escape_char(c)
|
|
||||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_nil(val)
|
|
||||||
return "null"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_table(val, stack)
|
|
||||||
local res = {}
|
|
||||||
stack = stack or {}
|
|
||||||
|
|
||||||
-- Circular reference?
|
|
||||||
if stack[val] then error("circular reference") end
|
|
||||||
|
|
||||||
stack[val] = true
|
|
||||||
|
|
||||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
|
||||||
-- Treat as array -- check keys are valid and it is not sparse
|
|
||||||
local n = 0
|
|
||||||
for k in pairs(val) do
|
|
||||||
if type(k) ~= "number" then
|
|
||||||
error("invalid table: mixed or invalid key types")
|
|
||||||
end
|
|
||||||
n = n + 1
|
|
||||||
end
|
|
||||||
if n ~= #val then
|
|
||||||
error("invalid table: sparse array")
|
|
||||||
end
|
|
||||||
-- Encode
|
|
||||||
for i, v in ipairs(val) do
|
|
||||||
table.insert(res, encode(v, stack))
|
|
||||||
end
|
|
||||||
stack[val] = nil
|
|
||||||
return "[" .. table.concat(res, ",") .. "]"
|
|
||||||
|
|
||||||
else
|
|
||||||
-- Treat as an object
|
|
||||||
for k, v in pairs(val) do
|
|
||||||
if type(k) ~= "string" then
|
|
||||||
error("invalid table: mixed or invalid key types")
|
|
||||||
end
|
|
||||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
|
||||||
end
|
|
||||||
stack[val] = nil
|
|
||||||
return "{" .. table.concat(res, ",") .. "}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_string(val)
|
|
||||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_number(val)
|
|
||||||
-- Check for NaN, -inf and inf
|
|
||||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
|
||||||
error("unexpected number value '" .. tostring(val) .. "'")
|
|
||||||
end
|
|
||||||
return string.format("%.14g", val)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local type_func_map = {
|
|
||||||
[ "nil" ] = encode_nil,
|
|
||||||
[ "table" ] = encode_table,
|
|
||||||
[ "string" ] = encode_string,
|
|
||||||
[ "number" ] = encode_number,
|
|
||||||
[ "boolean" ] = tostring,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
encode = function(val, stack)
|
|
||||||
local t = type(val)
|
|
||||||
local f = type_func_map[t]
|
|
||||||
if f then
|
|
||||||
return f(val, stack)
|
|
||||||
end
|
|
||||||
error("unexpected type '" .. t .. "'")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function json.encode(val)
|
|
||||||
return ( encode(val) )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Decode
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local parse
|
|
||||||
|
|
||||||
local function create_set(...)
|
|
||||||
local res = {}
|
|
||||||
for i = 1, select("#", ...) do
|
|
||||||
res[ select(i, ...) ] = true
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
|
||||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
|
||||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
|
||||||
local literals = create_set("true", "false", "null")
|
|
||||||
|
|
||||||
local literal_map = {
|
|
||||||
[ "true" ] = true,
|
|
||||||
[ "false" ] = false,
|
|
||||||
[ "null" ] = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local function next_char(str, idx, set, negate)
|
|
||||||
for i = idx, #str do
|
|
||||||
if set[str:sub(i, i)] ~= negate then
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return #str + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function decode_error(str, idx, msg)
|
|
||||||
local line_count = 1
|
|
||||||
local col_count = 1
|
|
||||||
for i = 1, idx - 1 do
|
|
||||||
col_count = col_count + 1
|
|
||||||
if str:sub(i, i) == "\n" then
|
|
||||||
line_count = line_count + 1
|
|
||||||
col_count = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function codepoint_to_utf8(n)
|
|
||||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
|
||||||
local f = math.floor
|
|
||||||
if n <= 0x7f then
|
|
||||||
return string.char(n)
|
|
||||||
elseif n <= 0x7ff then
|
|
||||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
|
||||||
elseif n <= 0xffff then
|
|
||||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
|
||||||
elseif n <= 0x10ffff then
|
|
||||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
|
||||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
|
||||||
end
|
|
||||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_unicode_escape(s)
|
|
||||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
|
||||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
|
||||||
-- Surrogate pair?
|
|
||||||
if n2 then
|
|
||||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
|
||||||
else
|
|
||||||
return codepoint_to_utf8(n1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_string(str, i)
|
|
||||||
local res = ""
|
|
||||||
local j = i + 1
|
|
||||||
local k = j
|
|
||||||
|
|
||||||
while j <= #str do
|
|
||||||
local x = str:byte(j)
|
|
||||||
|
|
||||||
if x < 32 then
|
|
||||||
decode_error(str, j, "control character in string")
|
|
||||||
|
|
||||||
elseif x == 92 then -- `\`: Escape
|
|
||||||
res = res .. str:sub(k, j - 1)
|
|
||||||
j = j + 1
|
|
||||||
local c = str:sub(j, j)
|
|
||||||
if c == "u" then
|
|
||||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
|
||||||
or str:match("^%x%x%x%x", j + 1)
|
|
||||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
|
||||||
res = res .. parse_unicode_escape(hex)
|
|
||||||
j = j + #hex
|
|
||||||
else
|
|
||||||
if not escape_chars[c] then
|
|
||||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
|
||||||
end
|
|
||||||
res = res .. escape_char_map_inv[c]
|
|
||||||
end
|
|
||||||
k = j + 1
|
|
||||||
|
|
||||||
elseif x == 34 then -- `"`: End of string
|
|
||||||
res = res .. str:sub(k, j - 1)
|
|
||||||
return res, j + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
j = j + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
decode_error(str, i, "expected closing quote for string")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_number(str, i)
|
|
||||||
local x = next_char(str, i, delim_chars)
|
|
||||||
local s = str:sub(i, x - 1)
|
|
||||||
local n = tonumber(s)
|
|
||||||
if not n then
|
|
||||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
|
||||||
end
|
|
||||||
return n, x
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_literal(str, i)
|
|
||||||
local x = next_char(str, i, delim_chars)
|
|
||||||
local word = str:sub(i, x - 1)
|
|
||||||
if not literals[word] then
|
|
||||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
|
||||||
end
|
|
||||||
return literal_map[word], x
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_array(str, i)
|
|
||||||
local res = {}
|
|
||||||
local n = 1
|
|
||||||
i = i + 1
|
|
||||||
while 1 do
|
|
||||||
local x
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
-- Empty / end of array?
|
|
||||||
if str:sub(i, i) == "]" then
|
|
||||||
i = i + 1
|
|
||||||
break
|
|
||||||
end
|
|
||||||
-- Read token
|
|
||||||
x, i = parse(str, i)
|
|
||||||
res[n] = x
|
|
||||||
n = n + 1
|
|
||||||
-- Next token
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
local chr = str:sub(i, i)
|
|
||||||
i = i + 1
|
|
||||||
if chr == "]" then break end
|
|
||||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
|
||||||
end
|
|
||||||
return res, i
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_object(str, i)
|
|
||||||
local res = {}
|
|
||||||
i = i + 1
|
|
||||||
while 1 do
|
|
||||||
local key, val
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
-- Empty / end of object?
|
|
||||||
if str:sub(i, i) == "}" then
|
|
||||||
i = i + 1
|
|
||||||
break
|
|
||||||
end
|
|
||||||
-- Read key
|
|
||||||
if str:sub(i, i) ~= '"' then
|
|
||||||
decode_error(str, i, "expected string for key")
|
|
||||||
end
|
|
||||||
key, i = parse(str, i)
|
|
||||||
-- Read ':' delimiter
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
if str:sub(i, i) ~= ":" then
|
|
||||||
decode_error(str, i, "expected ':' after key")
|
|
||||||
end
|
|
||||||
i = next_char(str, i + 1, space_chars, true)
|
|
||||||
-- Read value
|
|
||||||
val, i = parse(str, i)
|
|
||||||
-- Set
|
|
||||||
res[key] = val
|
|
||||||
-- Next token
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
local chr = str:sub(i, i)
|
|
||||||
i = i + 1
|
|
||||||
if chr == "}" then break end
|
|
||||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
|
||||||
end
|
|
||||||
return res, i
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local char_func_map = {
|
|
||||||
[ '"' ] = parse_string,
|
|
||||||
[ "0" ] = parse_number,
|
|
||||||
[ "1" ] = parse_number,
|
|
||||||
[ "2" ] = parse_number,
|
|
||||||
[ "3" ] = parse_number,
|
|
||||||
[ "4" ] = parse_number,
|
|
||||||
[ "5" ] = parse_number,
|
|
||||||
[ "6" ] = parse_number,
|
|
||||||
[ "7" ] = parse_number,
|
|
||||||
[ "8" ] = parse_number,
|
|
||||||
[ "9" ] = parse_number,
|
|
||||||
[ "-" ] = parse_number,
|
|
||||||
[ "t" ] = parse_literal,
|
|
||||||
[ "f" ] = parse_literal,
|
|
||||||
[ "n" ] = parse_literal,
|
|
||||||
[ "[" ] = parse_array,
|
|
||||||
[ "{" ] = parse_object,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
parse = function(str, idx)
|
|
||||||
local chr = str:sub(idx, idx)
|
|
||||||
local f = char_func_map[chr]
|
|
||||||
if f then
|
|
||||||
return f(str, idx)
|
|
||||||
end
|
|
||||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function json.decode(str)
|
|
||||||
if type(str) ~= "string" then
|
|
||||||
error("expected argument of type string, got " .. type(str))
|
|
||||||
end
|
|
||||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
|
||||||
idx = next_char(str, idx, space_chars, true)
|
|
||||||
if idx <= #str then
|
|
||||||
decode_error(str, idx, "trailing garbage")
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return json
|
|
||||||
|
|
@ -1,179 +0,0 @@
|
||||||
local keys = {
|
|
||||||
none = 0,
|
|
||||||
back = 8,
|
|
||||||
tab = 9,
|
|
||||||
enter = 13,
|
|
||||||
pause = 19,
|
|
||||||
caps_lock = 20,
|
|
||||||
kana = 21,
|
|
||||||
kanji = 25,
|
|
||||||
escape = 27,
|
|
||||||
ime_convert = 28,
|
|
||||||
ime_no_convert = 29,
|
|
||||||
space = 32,
|
|
||||||
page_up = 33,
|
|
||||||
page_down = 34,
|
|
||||||
["end"] = 35,
|
|
||||||
home = 36,
|
|
||||||
left = 37,
|
|
||||||
up = 38,
|
|
||||||
right = 39,
|
|
||||||
down = 40,
|
|
||||||
select = 41,
|
|
||||||
print = 42,
|
|
||||||
execute = 43,
|
|
||||||
print_screen = 44,
|
|
||||||
insert = 45,
|
|
||||||
delete = 46,
|
|
||||||
help = 47,
|
|
||||||
zero = 48,
|
|
||||||
one = 49,
|
|
||||||
two = 50,
|
|
||||||
three = 51,
|
|
||||||
four = 52,
|
|
||||||
five = 53,
|
|
||||||
six = 54,
|
|
||||||
seven = 55,
|
|
||||||
eight = 56,
|
|
||||||
nine = 57,
|
|
||||||
a = 65,
|
|
||||||
b = 66,
|
|
||||||
c = 67,
|
|
||||||
d = 68,
|
|
||||||
e = 69,
|
|
||||||
f = 70,
|
|
||||||
g = 71,
|
|
||||||
h = 72,
|
|
||||||
i = 73,
|
|
||||||
j = 74,
|
|
||||||
k = 75,
|
|
||||||
l = 76,
|
|
||||||
m = 77,
|
|
||||||
n = 78,
|
|
||||||
o = 79,
|
|
||||||
p = 80,
|
|
||||||
q = 81,
|
|
||||||
r = 82,
|
|
||||||
s = 83,
|
|
||||||
t = 84,
|
|
||||||
u = 85,
|
|
||||||
v = 86,
|
|
||||||
w = 87,
|
|
||||||
x = 88,
|
|
||||||
y = 89,
|
|
||||||
z = 90,
|
|
||||||
left_windows = 91,
|
|
||||||
right_windows = 92,
|
|
||||||
apps = 93,
|
|
||||||
sleep = 95,
|
|
||||||
num_pad0 = 96,
|
|
||||||
num_pad1 = 97,
|
|
||||||
num_pad2 = 98,
|
|
||||||
num_pad3 = 99,
|
|
||||||
num_pad4 = 100,
|
|
||||||
num_pad5 = 101,
|
|
||||||
num_pad6 = 102,
|
|
||||||
num_pad7 = 103,
|
|
||||||
num_pad8 = 104,
|
|
||||||
num_pad9 = 105,
|
|
||||||
multiply = 106,
|
|
||||||
add = 107,
|
|
||||||
separator = 108,
|
|
||||||
subtract = 109,
|
|
||||||
decimal = 110,
|
|
||||||
divide = 111,
|
|
||||||
f1 = 112,
|
|
||||||
f2 = 113,
|
|
||||||
f3 = 114,
|
|
||||||
f4 = 115,
|
|
||||||
f5 = 116,
|
|
||||||
f6 = 117,
|
|
||||||
f7 = 118,
|
|
||||||
f8 = 119,
|
|
||||||
f9 = 120,
|
|
||||||
f10 = 121,
|
|
||||||
f11 = 122,
|
|
||||||
f12 = 123,
|
|
||||||
f13 = 124,
|
|
||||||
f14 = 125,
|
|
||||||
f15 = 126,
|
|
||||||
f16 = 127,
|
|
||||||
f17 = 128,
|
|
||||||
f18 = 129,
|
|
||||||
f19 = 130,
|
|
||||||
f20 = 131,
|
|
||||||
f21 = 132,
|
|
||||||
f22 = 133,
|
|
||||||
f23 = 134,
|
|
||||||
f24 = 135,
|
|
||||||
num_lock = 144,
|
|
||||||
scroll = 145,
|
|
||||||
left_shift = 160,
|
|
||||||
right_shift = 161,
|
|
||||||
left_control = 162,
|
|
||||||
right_control = 163,
|
|
||||||
left_alt = 164,
|
|
||||||
right_alt = 165,
|
|
||||||
browser_back = 166,
|
|
||||||
browser_forward = 167,
|
|
||||||
browser_refresh = 168,
|
|
||||||
browser_stop = 169,
|
|
||||||
browser_search = 170,
|
|
||||||
browser_favorites = 171,
|
|
||||||
browser_home = 172,
|
|
||||||
volume_mute = 173,
|
|
||||||
volume_down = 174,
|
|
||||||
volume_up = 175,
|
|
||||||
media_next_track = 176,
|
|
||||||
media_previous_track = 177,
|
|
||||||
media_stop = 178,
|
|
||||||
media_play_pause = 179,
|
|
||||||
launch_mail = 180,
|
|
||||||
select_media = 181,
|
|
||||||
launch_application1 = 182,
|
|
||||||
launch_application2 = 183,
|
|
||||||
semicolon = 186,
|
|
||||||
plus = 187,
|
|
||||||
comma = 188,
|
|
||||||
minus = 189,
|
|
||||||
period = 190,
|
|
||||||
question = 191,
|
|
||||||
tilde = 192,
|
|
||||||
chat_pad_green = 202,
|
|
||||||
chat_pad_orange = 203,
|
|
||||||
open_brackets = 219,
|
|
||||||
pipe = 220,
|
|
||||||
close_brackets = 221,
|
|
||||||
quotes = 222,
|
|
||||||
oem8 = 223,
|
|
||||||
backslash = 226,
|
|
||||||
process_key = 229,
|
|
||||||
copy = 242,
|
|
||||||
auto = 243,
|
|
||||||
enl_w = 244,
|
|
||||||
attn = 246,
|
|
||||||
crsel = 247,
|
|
||||||
exsel = 248,
|
|
||||||
erase_eof = 249,
|
|
||||||
play = 250,
|
|
||||||
zoom = 251,
|
|
||||||
pa1 = 253,
|
|
||||||
clear = 254,
|
|
||||||
}
|
|
||||||
|
|
||||||
keys.mods = {
|
|
||||||
none = 0,
|
|
||||||
left_shift = 1,
|
|
||||||
right_shift = 2,
|
|
||||||
left_alt = 4,
|
|
||||||
right_alt = 8,
|
|
||||||
left_control = 16,
|
|
||||||
right_control = 32,
|
|
||||||
}
|
|
||||||
|
|
||||||
keys.mods.shift = keys.mods.left_shift | keys.mods.right_shift
|
|
||||||
keys.mods.alt = keys.mods.left_alt | keys.mods.right_alt
|
|
||||||
keys.mods.control = keys.mods.left_control | keys.mods.right_control
|
|
||||||
keys.mods.ctrl = keys.mods.control
|
|
||||||
|
|
||||||
return keys
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
local x = math.random()
|
|
||||||
|
|
||||||
return x
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
local expect = require("expect").expect
|
|
||||||
local tableutils = require("tableutils")
|
|
||||||
local event = require("event")
|
|
||||||
|
|
||||||
local scheduler = {}
|
|
||||||
|
|
||||||
local function contains(array, value)
|
|
||||||
for k, v in pairs(array) do
|
|
||||||
if v == value then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local tasks = {}
|
|
||||||
local processes = 0
|
|
||||||
|
|
||||||
local Task = {}
|
|
||||||
local TaskMeta = {
|
|
||||||
__index = Task,
|
|
||||||
__name = "OS_TASK",
|
|
||||||
__tostring = function(self)
|
|
||||||
return string.format("OS_TASK[%s]: %d", self.source or "", self.pid or 0)
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
local function newTask()
|
|
||||||
local task = {}
|
|
||||||
return setmetatable(task, TaskMeta)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Task:queue(eventName, ...)
|
|
||||||
expect(1, eventName, "string")
|
|
||||||
event.push("scheduler", self.pid, eventName, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function findParent()
|
|
||||||
local i = 3
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local info = debug.getinfo(i)
|
|
||||||
if not info then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
for pid, task in pairs(tasks) do
|
|
||||||
if task.uuid == tostring(info.func) then
|
|
||||||
return task
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function cascadeKill(pid, err)
|
|
||||||
local task = tasks[pid]
|
|
||||||
if not task then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
for i, cpid in ipairs(task.children) do
|
|
||||||
cascadeKill(cpid, err)
|
|
||||||
end
|
|
||||||
if task.parent then
|
|
||||||
local parent = tasks[task.parent]
|
|
||||||
if parent then
|
|
||||||
local index = tableutils.find(parent.children, task.pid)
|
|
||||||
table.remove(parent.children, index)
|
|
||||||
parent:queue("scheduler_task_end", task, err == nil, err)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if err then
|
|
||||||
error(err, 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if task then
|
|
||||||
task.killed = true
|
|
||||||
coroutine.close(task.thread)
|
|
||||||
tasks[pid] = nil
|
|
||||||
processes = processes - 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function resumeTask(task, yieldPars)
|
|
||||||
local pars = table.pack(coroutine.resume(task.thread, table.unpack(yieldPars)))
|
|
||||||
if pars[1] then
|
|
||||||
task.filters = table.pack(table.unpack(pars, 2))
|
|
||||||
return coroutine.status(task.thread) ~= "dead"
|
|
||||||
else
|
|
||||||
cascadeKill(task.pid, pars[2])
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function scheduler.spawn(func, options)
|
|
||||||
expect(1, func, "function")
|
|
||||||
expect(2, options, "nil", "table")
|
|
||||||
|
|
||||||
options = options or {}
|
|
||||||
options.args = options.args or {}
|
|
||||||
|
|
||||||
local source = debug.getinfo(2)
|
|
||||||
|
|
||||||
local task = newTask()
|
|
||||||
local pid = #tasks + 1
|
|
||||||
task.pid = pid
|
|
||||||
task.options = options
|
|
||||||
task.source = source.source
|
|
||||||
task.uuid = tostring(func)
|
|
||||||
task.thread = coroutine.create(func)
|
|
||||||
local parent = findParent()
|
|
||||||
if parent then
|
|
||||||
task.parent = parent.pid
|
|
||||||
table.insert(parent.children, pid)
|
|
||||||
end
|
|
||||||
task.filters = {}
|
|
||||||
task.children = {}
|
|
||||||
task.eventQueue = {}
|
|
||||||
task.skip = true
|
|
||||||
|
|
||||||
tasks[pid] = task
|
|
||||||
|
|
||||||
processes = processes + 1
|
|
||||||
|
|
||||||
return task, resumeTask(task, task.options.args)
|
|
||||||
end
|
|
||||||
|
|
||||||
function scheduler.kill(pid)
|
|
||||||
expect(1, pid, "number")
|
|
||||||
cascadeKill(pid)
|
|
||||||
end
|
|
||||||
|
|
||||||
function scheduler.ipc(pid, ...)
|
|
||||||
expect(1, pid, "number")
|
|
||||||
if not tasks[pid] then
|
|
||||||
error("process by pid " .. pid .. " does not exist.", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local sender = findParent()
|
|
||||||
tasks[pid]:queue("ipc_message", sender, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
local running = false
|
|
||||||
function scheduler.init()
|
|
||||||
if running then
|
|
||||||
error("scheduler already running", 2)
|
|
||||||
end
|
|
||||||
running = true
|
|
||||||
|
|
||||||
local ev = { n = 0 }
|
|
||||||
while processes > 0 do
|
|
||||||
for pid, task in pairs(tasks) do
|
|
||||||
local yieldPars = ev
|
|
||||||
if ev[1] == "scheduler" and ev[2] == pid then
|
|
||||||
yieldPars = table.pack(table.unpack(ev, 3))
|
|
||||||
end
|
|
||||||
if yieldPars[1] ~= "scheduler" and not task.filters or #task.filters == 0 or contains(task.filters, yieldPars[1]) or yieldPars[1] == "interrupt" then
|
|
||||||
if task.skip then
|
|
||||||
task.skip = false
|
|
||||||
else
|
|
||||||
resumeTask(task, yieldPars)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if coroutine.status(task.thread) == "dead" then
|
|
||||||
cascadeKill(pid)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if processes <= 0 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
ev = table.pack(coroutine.yield())
|
|
||||||
end
|
|
||||||
|
|
||||||
running = false
|
|
||||||
end
|
|
||||||
|
|
||||||
return scheduler
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
local expect = require("expect").expect
|
|
||||||
local fs = require("fs")
|
|
||||||
local nativePackage = package
|
|
||||||
|
|
||||||
local function copyTable(source, target)
|
|
||||||
target = target or {}
|
|
||||||
|
|
||||||
for k, v in pairs(source) do
|
|
||||||
target[k] = v
|
|
||||||
end
|
|
||||||
|
|
||||||
return target
|
|
||||||
end
|
|
||||||
|
|
||||||
local hostPackages = copyTable(nativePackage._host)
|
|
||||||
|
|
||||||
local function createPreloadSearcher(envPackage)
|
|
||||||
return function(name)
|
|
||||||
if not envPackage.preload[name] then
|
|
||||||
return string.format("no field package.preload['%s']", name)
|
|
||||||
end
|
|
||||||
return envPackage.preload[name], ":preload:"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function createLoaderSearcher(envPackage)
|
|
||||||
return function(name)
|
|
||||||
local path, err = envPackage.searchpath(name, envPackage.path)
|
|
||||||
|
|
||||||
if not path then
|
|
||||||
return err
|
|
||||||
end
|
|
||||||
|
|
||||||
local func, err = loadfile(path)
|
|
||||||
if not func then
|
|
||||||
return string.format("error loading module '%s' from file '%s':\t%s", name, path, err)
|
|
||||||
end
|
|
||||||
|
|
||||||
return func, path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function createEnvironment(filePath)
|
|
||||||
local envPackage = {
|
|
||||||
cpath = nativePackage.cpath,
|
|
||||||
searchpath = nativePackage.searchpath,
|
|
||||||
config = nativePackage.config,
|
|
||||||
searchers = {},
|
|
||||||
loaded = {},
|
|
||||||
preload = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
local dirName = fs.getDir(filePath)
|
|
||||||
--envPackage.path = string.format("%s/?.lua;%s/?/init.lua;", dirName, dirName) .. nativePackage.path
|
|
||||||
envPackage.path = nativePackage.path
|
|
||||||
|
|
||||||
envPackage.searchers[1] = createPreloadSearcher(envPackage)
|
|
||||||
envPackage.searchers[2] = createLoaderSearcher(envPackage)
|
|
||||||
|
|
||||||
local function envRequire(modname)
|
|
||||||
expect(1, modname, "string", "number")
|
|
||||||
modname = tostring(modname)
|
|
||||||
|
|
||||||
if envPackage.loaded[modname] then
|
|
||||||
return envPackage.loaded[modname]
|
|
||||||
end
|
|
||||||
|
|
||||||
local errorOutput = ""
|
|
||||||
local libFunction, libPath
|
|
||||||
for i = 1, #envPackage.searchers do
|
|
||||||
local par, path = envPackage.searchers[i](modname)
|
|
||||||
if type(par) == "function" then
|
|
||||||
libFunction, libPath = par, path
|
|
||||||
break
|
|
||||||
else
|
|
||||||
errorOutput = errorOutput .. "\n\t" .. par
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not libFunction then
|
|
||||||
error(string.format("module '%s' not found:%s", modname, errorOutput), 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, par = pcall(libFunction)
|
|
||||||
if not ok then
|
|
||||||
error(par, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
if par == nil then
|
|
||||||
envPackage.loaded[modname] = true
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
envPackage.loaded[modname] = par
|
|
||||||
|
|
||||||
return par, libPath
|
|
||||||
end
|
|
||||||
|
|
||||||
copyTable(hostPackages, envPackage.loaded)
|
|
||||||
envPackage.loaded.package = envPackage
|
|
||||||
|
|
||||||
local env_G = copyTable(envPackage.loaded._G or _G)
|
|
||||||
envPackage.loaded._G = env_G
|
|
||||||
env_G._G = env_G
|
|
||||||
|
|
||||||
envPackage.loaded._G.package = envPackage
|
|
||||||
envPackage.loaded._G.require = envRequire
|
|
||||||
|
|
||||||
return envPackage, envRequire
|
|
||||||
end
|
|
||||||
|
|
||||||
return createEnvironment
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
local expect = require("expect").expect
|
|
||||||
local tableutils = {}
|
|
||||||
|
|
||||||
local function serialize(data, circular)
|
|
||||||
expect(1, data, "string", "table", "number", "boolean", "nil")
|
|
||||||
if type(data) == "table" then
|
|
||||||
if not circular then
|
|
||||||
circular = {}
|
|
||||||
end
|
|
||||||
local output = "{"
|
|
||||||
for k, v in pairs(data) do
|
|
||||||
if type(v) == "table" then
|
|
||||||
local name = tostring(v)
|
|
||||||
if circular[name] then
|
|
||||||
error("circular reference in table", 2)
|
|
||||||
end
|
|
||||||
circular[name] = true
|
|
||||||
end
|
|
||||||
output = output .. string.format("[%q] = %s,", k, serialize(v, circular))
|
|
||||||
end
|
|
||||||
output = output .. "}"
|
|
||||||
return output
|
|
||||||
else
|
|
||||||
return string.format("%q", data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tableutils.serialize(data)
|
|
||||||
expect(1, data, "string", "table", "number", "boolean", "nil")
|
|
||||||
return serialize(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
function tableutils.deserialize(data)
|
|
||||||
local func, err = load("return " .. data, "=tableutils", "t", {})
|
|
||||||
if not func then
|
|
||||||
error(err, 2)
|
|
||||||
end
|
|
||||||
return func()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function prettyvalue(value)
|
|
||||||
if type(value) == "table" or type(value) == "function" or type(value) == "thread" or type(value) == "userdata" or type(value) == "number" then
|
|
||||||
return tostring(value)
|
|
||||||
else
|
|
||||||
return string.format("%q", value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tableutils.pretty(data)
|
|
||||||
if type(data) == "table" then
|
|
||||||
local output = "{"
|
|
||||||
|
|
||||||
local index = 0
|
|
||||||
for k, v in pairs(data) do
|
|
||||||
local value = prettyvalue(v)
|
|
||||||
|
|
||||||
if type(k) == "number" and k - 1 == index then
|
|
||||||
index = index + 1
|
|
||||||
output = output .. string.format("\n %s,", value)
|
|
||||||
elseif type(k) == "string" and k:match("^[%a_][%w_]*$") then
|
|
||||||
output = output .. string.format("\n %s = %s,", k, value)
|
|
||||||
else
|
|
||||||
output = output .. string.format("\n [%s] = %s,", prettyvalue(k), value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if output == "{" then
|
|
||||||
return "{}"
|
|
||||||
end
|
|
||||||
output = output .. "\n}"
|
|
||||||
|
|
||||||
return output
|
|
||||||
else
|
|
||||||
return prettyvalue(data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tableutils.find(tbl, element)
|
|
||||||
for i = 1, #tbl do
|
|
||||||
if tbl[i] == element then
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return tableutils
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,2 +0,0 @@
|
||||||
Welcome to CapyOS!
|
|
||||||
Run "programs" to get a list of available programs.
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
CapyOS and Capy64 are licensed under the Apache 2.0 Public License.
|
|
||||||
https://capy64.alexdevs.me/
|
|
||||||
https://github.com/Ale32bit/Capy64
|
|
||||||
|
|
||||||
Some CapyOS components include code from the CC Recrafted project.
|
|
||||||
https://github.com/Ocawesome101/recrafted
|
|
||||||
|
|
||||||
Some CapyOS components may include code from CC: Tweaked.
|
|
||||||
https://github.com/CC-Tweaked/CC-Tweaked
|
|
||||||
|
|
||||||
json.lua by rxi is licensed under the MIT License.
|
|
||||||
https://github.com/rxi/json.lua
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
You can get started with Lua by following this manual: https://www.lua.org/manual/
|
|
||||||
Learn more about Capy64 by following the documentation: https://capy64.alexdevs.me/
|
|
||||||
Found a bug or would like to suggest a feature? https://github.com/Ale32bit/Capy64/issues
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
|
|
@ -1,5 +0,0 @@
|
||||||
If you are looking for your data directory look in:
|
|
||||||
|
|
||||||
Windows: %APPDATA%\Capy64\data
|
|
||||||
Mac: $HOME/.local/share/Capy64/data
|
|
||||||
Linux: $HOME/.local/share/Capy64/data
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
-- This file is part of Capy64 - https://github.com/Capy64/Capy64
|
|
||||||
-- Copyright 2023 Alessandro "AlexDevs" Proto
|
|
||||||
--
|
|
||||||
-- Licensed under the Apache License, Version 2.0 (the "License").
|
|
||||||
-- you may not use this file except in compliance with the License.
|
|
||||||
-- You may obtain a copy of the License at
|
|
||||||
--
|
|
||||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
--
|
|
||||||
-- Unless required by applicable law or agreed to in writing, software
|
|
||||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
-- See the License for the specific language governing permissions and
|
|
||||||
-- limitations under the License.
|
|
||||||
|
|
||||||
local event = require("event")
|
|
||||||
local coroutine = coroutine
|
|
||||||
|
|
||||||
-- Declare event functions
|
|
||||||
function event.pull(...)
|
|
||||||
local ev = table.pack(coroutine.yield(...))
|
|
||||||
if ev[1] == "interrupt" then
|
|
||||||
error("Interrupted", 2)
|
|
||||||
end
|
|
||||||
return table.unpack(ev)
|
|
||||||
end
|
|
||||||
|
|
||||||
function event.pullRaw(...)
|
|
||||||
return coroutine.yield(...)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set task awaiter
|
|
||||||
|
|
||||||
local function awaiter(task)
|
|
||||||
local status = task:getStatus()
|
|
||||||
local uuid = task:getID()
|
|
||||||
if status == "running" then
|
|
||||||
local _, taskId, result, err
|
|
||||||
repeat
|
|
||||||
_, taskId, result, err = event.pull("task_finish")
|
|
||||||
until taskId == uuid
|
|
||||||
return result, err
|
|
||||||
elseif status == "succeeded" then
|
|
||||||
return task:getResult(), nil
|
|
||||||
elseif status == "failed" then
|
|
||||||
return nil, task:getError()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Second argument freezes the awaiter function, so it cannot be modified
|
|
||||||
event.setAwaiter(awaiter, true)
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
-- This file is part of Capy64 - https://github.com/Capy64/Capy64
|
-- This file is part of Capy64 - https://github.com/Capy64/Capy64
|
||||||
-- Copyright 2023 Alessandro "AlexDevs" Proto
|
-- Copyright 2023 Alessandro "AlexDevs" Proto
|
||||||
--
|
--
|
||||||
-- Licensed under the Apache License, Version 2.0 (the "License").
|
-- Licensed under the Apache License, Version 2.0 (the "License").
|
||||||
|
|
@ -19,22 +19,22 @@ local gpu = require("gpu")
|
||||||
local fs = require("fs")
|
local fs = require("fs")
|
||||||
local machine = require("machine")
|
local machine = require("machine")
|
||||||
local audio = require("audio")
|
local audio = require("audio")
|
||||||
|
local http = require("http")
|
||||||
local event = require("event")
|
local event = require("event")
|
||||||
|
|
||||||
local bootSleep = 2
|
|
||||||
|
local INDEX_URL = "https://raw.github.com/Ale32bit/CapyOS/deploy/index.json"
|
||||||
|
local JSON_URL = "https://raw.github.com/Ale32bit/CapyOS/main/lib/json.lua"
|
||||||
|
|
||||||
|
local bootSleep = 2000
|
||||||
local bg = 0x0
|
local bg = 0x0
|
||||||
local fg = 0xffffff
|
local fg = 0xffffff
|
||||||
local setupbg = 0x0608a6
|
|
||||||
local setupfg = 0xffffff
|
|
||||||
local accent = 0xffea00
|
|
||||||
|
|
||||||
term.setForeground(fg)
|
term.setForeground(fg)
|
||||||
term.setBackground(bg)
|
term.setBackground(bg)
|
||||||
term.clear()
|
term.clear()
|
||||||
|
|
||||||
if term.isResizable() then
|
term.setSize(53, 20)
|
||||||
term.setSize(53, 20)
|
|
||||||
end
|
|
||||||
|
|
||||||
local w, h = term.getSize()
|
local w, h = term.getSize()
|
||||||
|
|
||||||
|
|
@ -56,13 +56,13 @@ local function writeCenter(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function drawVendorImage()
|
local function drawVendorImage()
|
||||||
if not fs.exists("/sys/vendor.bmp") then
|
if not fs.exists("/boot/vendor.bmp") then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local w, h = gpu.getSize()
|
local w, h = gpu.getSize()
|
||||||
local ok, err = pcall(function()
|
local ok, err = pcall(function()
|
||||||
local task<close> = gpu.loadImageAsync("/sys/vendor.bmp")
|
local task<close> = gpu.loadImageAsync("/boot/vendor.bmp")
|
||||||
local buffer<close> = task:await()
|
local buffer<close> = task:await()
|
||||||
|
|
||||||
local x, y =
|
local x, y =
|
||||||
|
|
@ -95,65 +95,62 @@ local function printError( text )
|
||||||
term.setForeground(0xffffff)
|
term.setForeground(0xffffff)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function hget(url, headers)
|
||||||
|
local response, err = http.requestAsync(url, nul, headers, {binary = true}):await()
|
||||||
|
|
||||||
|
if not response then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
local content = response.content:read("a")
|
||||||
|
response.content:close()
|
||||||
|
return content, response
|
||||||
|
end
|
||||||
|
|
||||||
local function promptKey()
|
local function promptKey()
|
||||||
print("Press any key to continue")
|
print("Press any key to continue")
|
||||||
event.pull("key_down")
|
event.pull("key_down")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function alert(...)
|
local function installOS()
|
||||||
local args = {...}
|
term.clear()
|
||||||
table.insert(args, "[ OK ]")
|
term.setPos(1, 1)
|
||||||
local lines = {}
|
|
||||||
local width = 0
|
print("Installing CapyOS...")
|
||||||
local padding = 1
|
|
||||||
for k, v in ipairs(args) do
|
local jsonLib, par = hget(JSON_URL)
|
||||||
lines[k] = tostring(v)
|
if not jsonLib then
|
||||||
width = math.max(width, #tostring(v))
|
printError(par)
|
||||||
|
promptKey()
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
lines[#lines] = nil
|
local json = load(jsonLib)()
|
||||||
|
local indexData, par = hget(INDEX_URL)
|
||||||
local okPad = string.rep(" ", (width - #args[#args]) // 2)
|
if not indexData then
|
||||||
local okLine = string.format("%s%s", okPad, args[#args])
|
printError(par)
|
||||||
|
promptKey()
|
||||||
local w, h = term.getSize()
|
return
|
||||||
local cx, cy = w//2, h//2
|
|
||||||
local dx, dy = cx - (width + padding * 2) // 2, cy - #lines//2 - 1
|
|
||||||
|
|
||||||
local pad = string.rep(" ", padding)
|
|
||||||
local emptyLine = string.format("\u{258C}%s%s%s\u{2590}", pad, string.rep(" ", width), pad)
|
|
||||||
|
|
||||||
term.setPos(dx, dy)
|
|
||||||
print("\u{259B}" .. string.rep("\u{2580}", width + padding * 2) .. "\u{259C}")
|
|
||||||
for k, v in ipairs(lines) do
|
|
||||||
term.setPos(dx, dy + k)
|
|
||||||
local space = string.rep(" ", width - #v)
|
|
||||||
print(string.format("\u{258C}%s%s%s%s\u{2590}", pad, v, space, pad))
|
|
||||||
end
|
end
|
||||||
term.setPos(dx, dy + #lines + 1)
|
local index = json.decode(indexData)
|
||||||
print(emptyLine)
|
|
||||||
term.setPos(dx, dy + #lines + 2)
|
|
||||||
local space = string.rep(" ", width - #okLine)
|
|
||||||
term.write(string.format("\u{258C}%s", pad))
|
|
||||||
term.setForeground(accent)
|
|
||||||
term.write(okLine)
|
|
||||||
term.setForeground(setupfg)
|
|
||||||
print(string.format("%s%s\u{2590}", space, pad))
|
|
||||||
term.setPos(dx, dy + #lines + 3)
|
|
||||||
print("\u{2599}" .. string.rep("\u{2584}", width + padding * 2) .. "\u{259F}")
|
|
||||||
|
|
||||||
local _, key, keyname
|
for i, v in ipairs(index) do
|
||||||
repeat
|
local dirname = fs.getDir(v.path)
|
||||||
_, key, keyname = event.pull("key_down")
|
if not fs.exists(dirname) then
|
||||||
until keyname == "enter" or keyname == "escape"
|
fs.makeDir(dirname)
|
||||||
end
|
end
|
||||||
|
print("Downloading " .. v.path)
|
||||||
local function installDefaultOS()
|
local fileContent = hget(v.raw_url)
|
||||||
if fs.exists("/sys") then
|
local f = fs.open(v.path, "w")
|
||||||
fs.delete("/sys", true)
|
f:write(fileContent)
|
||||||
|
f:close()
|
||||||
|
print("Written to " .. v.path)
|
||||||
end
|
end
|
||||||
installOS()
|
|
||||||
alert("Default OS installed!")
|
flagInstalled()
|
||||||
|
|
||||||
|
print("CapyOS installed!")
|
||||||
|
promptKey()
|
||||||
end
|
end
|
||||||
|
|
||||||
term.setBlink(false)
|
term.setBlink(false)
|
||||||
|
|
@ -166,9 +163,8 @@ local function setupScreen()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Install default OS",
|
"Install default OS",
|
||||||
installDefaultOS,
|
installOS,
|
||||||
},
|
},
|
||||||
{},
|
|
||||||
{
|
{
|
||||||
"Exit setup",
|
"Exit setup",
|
||||||
exit,
|
exit,
|
||||||
|
|
@ -180,40 +176,26 @@ local function setupScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
local selection = 1
|
local selection = 1
|
||||||
local function redraw(noDrawSel)
|
local function redraw()
|
||||||
local w, h = term.getSize()
|
term.setForeground(fg)
|
||||||
term.setForeground(setupfg)
|
term.setBackground(bg)
|
||||||
term.setBackground(setupbg)
|
|
||||||
term.clear()
|
term.clear()
|
||||||
term.setPos(1,2)
|
term.setPos(1,2)
|
||||||
writeCenter("Capy64 Setup")
|
writeCenter("Capy64 Setup")
|
||||||
|
|
||||||
term.setPos(1,3)
|
term.setPos(1,3)
|
||||||
|
|
||||||
term.setForeground(accent)
|
|
||||||
|
|
||||||
for k, v in ipairs(options) do
|
for k, v in ipairs(options) do
|
||||||
local _, y = term.getPos()
|
local _, y = term.getPos()
|
||||||
term.setPos(1, y + 1)
|
term.setPos(1, y + 1)
|
||||||
term.clearLine()
|
term.clearLine()
|
||||||
|
|
||||||
if #v > 0 then
|
if selection == k then
|
||||||
if selection == k and not noDrawSel then
|
writeCenter("[ " .. v[1] .. " ]")
|
||||||
writeCenter("[ " .. v[1] .. " ]")
|
else
|
||||||
else
|
writeCenter(v[1])
|
||||||
writeCenter(v[1])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
term.setForeground(setupfg)
|
|
||||||
|
|
||||||
term.setPos(1, h - 2)
|
|
||||||
term.write("\u{2190}\u{2191}\u{2192}\u{2193}: Move")
|
|
||||||
term.setPos(1, h - 1)
|
|
||||||
term.write("Escape: Exit")
|
|
||||||
term.setPos(1, h)
|
|
||||||
term.write("Enter: Select")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
|
|
@ -221,16 +203,9 @@ local function setupScreen()
|
||||||
local ev = { coroutine.yield("key_down") }
|
local ev = { coroutine.yield("key_down") }
|
||||||
if ev[3] == "up" then
|
if ev[3] == "up" then
|
||||||
selection = selection - 1
|
selection = selection - 1
|
||||||
if options[selection] and #options[selection] == 0 then
|
|
||||||
selection = selection - 1
|
|
||||||
end
|
|
||||||
elseif ev[3] == "down" then
|
elseif ev[3] == "down" then
|
||||||
selection = selection + 1
|
selection = selection + 1
|
||||||
if options[selection] and #options[selection] == 0 then
|
|
||||||
selection = selection + 1
|
|
||||||
end
|
|
||||||
elseif ev[3] == "enter" then
|
elseif ev[3] == "enter" then
|
||||||
redraw(true)
|
|
||||||
options[selection][2]()
|
options[selection][2]()
|
||||||
elseif ev[3] == "escape" then
|
elseif ev[3] == "escape" then
|
||||||
exit()
|
exit()
|
||||||
|
|
@ -251,7 +226,7 @@ local function bootScreen()
|
||||||
term.setPos(1,2)
|
term.setPos(1,2)
|
||||||
writeCenter("Capy64")
|
writeCenter("Capy64")
|
||||||
term.setPos(1,4)
|
term.setPos(1,4)
|
||||||
writeCenter("(c) 2023 AlexDevs")
|
writeCenter("Powered by Capybaras")
|
||||||
|
|
||||||
term.setPos(1, h - 1)
|
term.setPos(1, h - 1)
|
||||||
writeCenter("Press F2 to open setup")
|
writeCenter("Press F2 to open setup")
|
||||||
|
|
@ -270,6 +245,10 @@ end
|
||||||
|
|
||||||
audio.beep(1000, 0.2, 0.2, "square")
|
audio.beep(1000, 0.2, 0.2, "square")
|
||||||
|
|
||||||
|
if shouldInstallOS() then
|
||||||
|
installOS()
|
||||||
|
end
|
||||||
|
|
||||||
bootScreen()
|
bootScreen()
|
||||||
|
|
||||||
term.clear()
|
term.clear()
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"EngineMode": 0,
|
"EngineMode": 0,
|
||||||
"SafeMode": false,
|
|
||||||
"Window": {
|
"Window": {
|
||||||
"Scale": 2
|
"Scale": 2
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -21,15 +21,14 @@ using Capy64.Integrations;
|
||||||
using Capy64.PluginManager;
|
using Capy64.PluginManager;
|
||||||
using Capy64.Runtime;
|
using Capy64.Runtime;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using Microsoft.Xna.Framework.Input;
|
|
||||||
using MonoGame.Extended;
|
using MonoGame.Extended;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static Capy64.Utils;
|
using static Capy64.Utils;
|
||||||
|
|
||||||
|
|
@ -41,9 +40,10 @@ public enum EngineMode
|
||||||
Free
|
Free
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Capy64 : Game
|
|
||||||
|
public class Capy64 : Game, IGame
|
||||||
{
|
{
|
||||||
public const string Version = "1.1.2-beta";
|
public const string Version = "1.0.0-beta";
|
||||||
|
|
||||||
public static class DefaultParameters
|
public static class DefaultParameters
|
||||||
{
|
{
|
||||||
|
|
@ -53,12 +53,10 @@ public class Capy64 : Game
|
||||||
public const float BorderMultiplier = 1.5f;
|
public const float BorderMultiplier = 1.5f;
|
||||||
public static readonly EngineMode EngineMode = EngineMode.Classic;
|
public static readonly EngineMode EngineMode = EngineMode.Classic;
|
||||||
|
|
||||||
public const int ClassicTickrate = 30;
|
public const int ClassicTickrate = 20;
|
||||||
public const int FreeTickrate = 60;
|
public const int FreeTickrate = 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
|
||||||
public static readonly string AssetsPath = Path.Combine(AssemblyPath, "Assets");
|
|
||||||
|
|
||||||
public static string AppDataPath
|
public static string AppDataPath
|
||||||
{
|
{
|
||||||
|
|
@ -66,15 +64,12 @@ public class Capy64 : Game
|
||||||
{
|
{
|
||||||
string baseDir =
|
string baseDir =
|
||||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData,
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create) :
|
||||||
Environment.SpecialFolderOption.Create) :
|
|
||||||
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
|
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create) :
|
||||||
Environment.SpecialFolderOption.Create) :
|
|
||||||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
|
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create) :
|
||||||
Environment.SpecialFolderOption.Create) :
|
"./";
|
||||||
"./";
|
|
||||||
|
|
||||||
return Path.Combine(baseDir, "Capy64");
|
return Path.Combine(baseDir, "Capy64");
|
||||||
}
|
}
|
||||||
|
|
@ -93,11 +88,8 @@ public class Capy64 : Game
|
||||||
public LuaState LuaRuntime { get; set; }
|
public LuaState LuaRuntime { get; set; }
|
||||||
public Eventing.EventEmitter EventEmitter { get; private set; }
|
public Eventing.EventEmitter EventEmitter { get; private set; }
|
||||||
public DiscordIntegration Discord { get; set; }
|
public DiscordIntegration Discord { get; set; }
|
||||||
public int TickRate => tickrate;
|
|
||||||
public IConfiguration Configuration { get; private set; }
|
|
||||||
|
|
||||||
public Color BorderColor { get; set; } = Color.Black;
|
public Color BorderColor { get; set; } = Color.Black;
|
||||||
|
|
||||||
public Borders Borders = new()
|
public Borders Borders = new()
|
||||||
{
|
{
|
||||||
Top = 0,
|
Top = 0,
|
||||||
|
|
@ -105,23 +97,22 @@ public class Capy64 : Game
|
||||||
Left = 0,
|
Left = 0,
|
||||||
Right = 0,
|
Right = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
public SpriteBatch SpriteBatch;
|
public SpriteBatch SpriteBatch;
|
||||||
|
|
||||||
|
|
||||||
private readonly InputManager _inputManager;
|
private readonly InputManager _inputManager;
|
||||||
private RenderTarget2D renderTarget;
|
private RenderTarget2D renderTarget;
|
||||||
private readonly GraphicsDeviceManager _graphics;
|
private readonly GraphicsDeviceManager _graphics;
|
||||||
|
private IServiceProvider _serviceProvider;
|
||||||
private ulong _totalTicks = 0;
|
private ulong _totalTicks = 0;
|
||||||
private int tickrate = 0;
|
private ulong tickrate = 0;
|
||||||
private int everyTick => 60 / tickrate;
|
|
||||||
|
|
||||||
public Capy64()
|
public Capy64()
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instance = this;
|
||||||
|
|
||||||
_graphics = new GraphicsDeviceManager(this);
|
_graphics = new GraphicsDeviceManager(this);
|
||||||
//Content.RootDirectory = "Content";
|
Content.RootDirectory = "Content";
|
||||||
IsMouseVisible = true;
|
IsMouseVisible = true;
|
||||||
|
|
||||||
EventEmitter = new();
|
EventEmitter = new();
|
||||||
|
|
@ -130,6 +121,11 @@ public class Capy64 : Game
|
||||||
Drawing = new();
|
Drawing = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ConfigureServices(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetEngineMode(EngineMode mode)
|
public void SetEngineMode(EngineMode mode)
|
||||||
{
|
{
|
||||||
switch (mode)
|
switch (mode)
|
||||||
|
|
@ -148,9 +144,6 @@ public class Capy64 : Game
|
||||||
Window.AllowUserResizing = true;
|
Window.AllowUserResizing = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
EngineMode = mode;
|
|
||||||
|
|
||||||
UpdateSize(true);
|
UpdateSize(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,12 +173,6 @@ public class Capy64 : Game
|
||||||
|
|
||||||
private void OnWindowSizeChange(object sender, EventArgs e)
|
private void OnWindowSizeChange(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (EngineMode == EngineMode.Classic)
|
|
||||||
{
|
|
||||||
UpdateSize(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bounds = Window.ClientBounds;
|
var bounds = Window.ClientBounds;
|
||||||
|
|
||||||
Width = (int)(bounds.Width / Scale);
|
Width = (int)(bounds.Width / Scale);
|
||||||
|
|
@ -209,6 +196,7 @@ public class Capy64 : Game
|
||||||
ResetBorder();
|
ResetBorder();
|
||||||
UpdateSize();
|
UpdateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetBorder()
|
private void ResetBorder()
|
||||||
|
|
@ -225,26 +213,11 @@ public class Capy64 : Game
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
var configBuilder = new ConfigurationBuilder();
|
var configuration = _serviceProvider.GetService<IConfiguration>();
|
||||||
|
|
||||||
var settingsPath = Path.Combine(AppDataPath, "settings.json");
|
|
||||||
if (!Directory.Exists(AppDataPath))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(AppDataPath);
|
|
||||||
}
|
|
||||||
if (!File.Exists(settingsPath))
|
|
||||||
{
|
|
||||||
File.Copy(Path.Combine(AssetsPath, "default.json"), settingsPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
configBuilder.AddJsonFile(Path.Combine(AssetsPath, "default.json"), false);
|
|
||||||
configBuilder.AddJsonFile(settingsPath, false);
|
|
||||||
|
|
||||||
Configuration = configBuilder.Build();
|
|
||||||
|
|
||||||
Window.Title = "Capy64 " + Version;
|
Window.Title = "Capy64 " + Version;
|
||||||
|
|
||||||
Scale = Configuration.GetValue("Window:Scale", DefaultParameters.Scale);
|
Scale = configuration.GetValue("Window:Scale", DefaultParameters.Scale);
|
||||||
|
|
||||||
ResetBorder();
|
ResetBorder();
|
||||||
UpdateSize();
|
UpdateSize();
|
||||||
|
|
@ -254,14 +227,12 @@ public class Capy64 : Game
|
||||||
|
|
||||||
InactiveSleepTime = new TimeSpan(0);
|
InactiveSleepTime = new TimeSpan(0);
|
||||||
|
|
||||||
SetEngineMode(Configuration.GetValue<EngineMode>("EngineMode", DefaultParameters.EngineMode));
|
SetEngineMode(configuration.GetValue<EngineMode>("EngineMode", DefaultParameters.EngineMode));
|
||||||
|
|
||||||
Audio = new Audio();
|
Audio = new Audio();
|
||||||
|
|
||||||
NativePlugins = GetNativePlugins();
|
NativePlugins = GetNativePlugins();
|
||||||
var safeMode = Configuration.GetValue("SafeMode", false);
|
Plugins = PluginLoader.LoadAllPlugins("plugins", _serviceProvider);
|
||||||
if (!safeMode)
|
|
||||||
Plugins = PluginLoader.LoadAllPlugins(Path.Combine(AppDataPath, "plugins"));
|
|
||||||
|
|
||||||
EventEmitter.RaiseInit();
|
EventEmitter.RaiseInit();
|
||||||
|
|
||||||
|
|
@ -279,10 +250,9 @@ public class Capy64 : Game
|
||||||
|
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
var instance = (IComponent)Activator.CreateInstance(type, this);
|
var instance = (IComponent)ActivatorUtilities.CreateInstance(_serviceProvider, type)!;
|
||||||
plugins.Add(instance);
|
plugins.Add(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
return plugins;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,7 +273,7 @@ public class Capy64 : Game
|
||||||
{
|
{
|
||||||
GameTime = gameTime,
|
GameTime = gameTime,
|
||||||
TotalTicks = _totalTicks,
|
TotalTicks = _totalTicks,
|
||||||
IsActiveTick = (int)_totalTicks % everyTick == 0,
|
IsActiveTick = _totalTicks % (60 / tickrate) == 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
Drawing.End();
|
Drawing.End();
|
||||||
|
|
@ -319,11 +289,9 @@ public class Capy64 : Game
|
||||||
GraphicsDevice.Clear(BorderColor);
|
GraphicsDevice.Clear(BorderColor);
|
||||||
|
|
||||||
SpriteBatch.DrawRectangle(renderTarget.Bounds.Location.ToVector2() + new Vector2(Borders.Left, Borders.Top),
|
SpriteBatch.DrawRectangle(renderTarget.Bounds.Location.ToVector2() + new Vector2(Borders.Left, Borders.Top),
|
||||||
new Size2(renderTarget.Bounds.Width * Scale, renderTarget.Bounds.Height * Scale), Color.Black,
|
new Size2(renderTarget.Bounds.Width * Scale, renderTarget.Bounds.Height * Scale), Color.Black, Math.Min(renderTarget.Bounds.Width, renderTarget.Bounds.Height), 0);
|
||||||
Math.Min(renderTarget.Bounds.Width, renderTarget.Bounds.Height), 0);
|
|
||||||
|
|
||||||
SpriteBatch.Draw(renderTarget, new(Borders.Left, Borders.Top), null, Color.White, 0f, Vector2.Zero, Scale,
|
SpriteBatch.Draw(renderTarget, new(Borders.Left, Borders.Top), null, Color.White, 0f, Vector2.Zero, Scale, SpriteEffects.None, 0);
|
||||||
SpriteEffects.None, 0);
|
|
||||||
|
|
||||||
EventEmitter.RaiseOverlay(new()
|
EventEmitter.RaiseOverlay(new()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<RollForward>Major</RollForward>
|
<RollForward>Major</RollForward>
|
||||||
<PublishReadyToRun>false</PublishReadyToRun>
|
<PublishReadyToRun>false</PublishReadyToRun>
|
||||||
<TieredCompilation>false</TieredCompilation>
|
<TieredCompilation>false</TieredCompilation>
|
||||||
|
|
@ -26,9 +26,9 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Assets\Lua\**">
|
<None Update="Assets\Lua/**">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\LICENSE">
|
<None Include="..\LICENSE">
|
||||||
|
|
@ -37,22 +37,26 @@
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
<PackageReference Include="FontStashSharp.MonoGame" Version="1.3.6" />
|
<PackageReference Include="FontStashSharp.MonoGame" Version="1.2.8" />
|
||||||
<PackageReference Include="KeraLua" Version="1.4.1" />
|
<PackageReference Include="KeraLua" Version="1.3.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
|
||||||
<PackageReference Include="MonoGame.Extended.Graphics" Version="3.8.0" />
|
<PackageReference Include="MonoGame.Extended.Graphics" Version="3.8.0" />
|
||||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
||||||
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
|
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
|
||||||
|
<PackageReference Include="System.ComponentModel.Composition" Version="7.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EditorConfigFiles Remove="C:\Users\Alex\source\repos\Capy64\Capy64\Capy64\.editorconfig" />
|
<None Update="Assets\bios.lua">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
|
||||||
|
<Message Text="Restoring dotnet tools" Importance="High" />
|
||||||
|
<Exec Command="dotnet tool restore" />
|
||||||
|
</Target>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Assets\Lua\" />
|
<EditorConfigFiles Remove="C:\Users\Alex\source\repos\Capy64\Capy64\Capy64\.editorconfig" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
|
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
|
||||||
|
|
|
||||||
15
Capy64/Content/Content.mgcb
Normal file
15
Capy64/Content/Content.mgcb
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
#----------------------------- Global Properties ----------------------------#
|
||||||
|
|
||||||
|
/outputDir:bin/$(Platform)
|
||||||
|
/intermediateDir:obj/$(Platform)
|
||||||
|
/platform:DesktopGL
|
||||||
|
/config:
|
||||||
|
/profile:Reach
|
||||||
|
/compress:False
|
||||||
|
|
||||||
|
#-------------------------------- References --------------------------------#
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------- Content ---------------------------------#
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ public class Audio : IDisposable
|
||||||
Noise
|
Noise
|
||||||
}
|
}
|
||||||
|
|
||||||
public const int SampleRate = 24000;
|
public const int SampleRate = 16000;
|
||||||
public const int HQSampleRate = 48000;
|
public const int HQSampleRate = 48000;
|
||||||
public const AudioChannels AudioChannel = AudioChannels.Mono;
|
public const AudioChannels AudioChannel = AudioChannels.Mono;
|
||||||
public const int ChannelsCount = 8;
|
public const int ChannelsCount = 8;
|
||||||
|
|
@ -126,7 +126,6 @@ public class Audio : IDisposable
|
||||||
|
|
||||||
public TimeSpan SubmitHQ(byte[] buffer)
|
public TimeSpan SubmitHQ(byte[] buffer)
|
||||||
{
|
{
|
||||||
|
|
||||||
HQChannel.SubmitBuffer(buffer);
|
HQChannel.SubmitBuffer(buffer);
|
||||||
return HQChannel.GetSampleDuration(buffer.Length);
|
return HQChannel.GetSampleDuration(buffer.Length);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ public class Drawing : IDisposable
|
||||||
public Drawing()
|
public Drawing()
|
||||||
{
|
{
|
||||||
_fontSystem = new FontSystem();
|
_fontSystem = new FontSystem();
|
||||||
_fontSystem.AddFont(File.ReadAllBytes(Path.Combine(Capy64.AssetsPath, "font.ttf")));
|
_fontSystem.AddFont(File.ReadAllBytes(@"Assets/font.ttf"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Begin()
|
public void Begin()
|
||||||
|
|
@ -201,7 +201,7 @@ public class Drawing : IDisposable
|
||||||
_spriteBatch.Draw(_whitePixel, position3, null, color, rotation, Vector2.Zero, scale, SpriteEffects.None, layerDepth);
|
_spriteBatch.Draw(_whitePixel, position3, null, color, rotation, Vector2.Zero, scale, SpriteEffects.None, layerDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawBuffer(uint[] buffer, Rectangle rect, Rectangle? source = null, Color? color = null, float rotation = 0f, Vector2? origin = null, Vector2? scale = null, SpriteEffects spriteEffects = 0)
|
public void DrawBuffer(uint[] buffer, Rectangle rect, Rectangle? source = null, Color? color = null, float rotation = 0f, Vector2? origin = null, float scale = 1f, SpriteEffects spriteEffects = 0)
|
||||||
{
|
{
|
||||||
var texture = new Texture2D(_graphicsDevice, rect.Width, rect.Height, false, SurfaceFormat.Color);
|
var texture = new Texture2D(_graphicsDevice, rect.Width, rect.Height, false, SurfaceFormat.Color);
|
||||||
texture.SetData(buffer);
|
texture.SetData(buffer);
|
||||||
|
|
@ -213,7 +213,7 @@ public class Drawing : IDisposable
|
||||||
color ?? Color.White, // Color
|
color ?? Color.White, // Color
|
||||||
rotation, // Rotation
|
rotation, // Rotation
|
||||||
origin ?? Vector2.Zero, // Origin
|
origin ?? Vector2.Zero, // Origin
|
||||||
scale ?? Vector2.One, // Scale
|
scale, // Scale
|
||||||
spriteEffects, // Flip effects
|
spriteEffects, // Flip effects
|
||||||
0f // layer depth
|
0f // layer depth
|
||||||
);
|
);
|
||||||
|
|
|
||||||
50
Capy64/IGame.cs
Normal file
50
Capy64/IGame.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||||
|
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
using Capy64.API;
|
||||||
|
using Capy64.Core;
|
||||||
|
using Capy64.Integrations;
|
||||||
|
using Capy64.Runtime;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Capy64;
|
||||||
|
|
||||||
|
public interface IGame
|
||||||
|
{
|
||||||
|
Capy64 Game { get; }
|
||||||
|
EngineMode EngineMode { get; }
|
||||||
|
IList<IComponent> NativePlugins { get; }
|
||||||
|
IList<IComponent> Plugins { get; }
|
||||||
|
GameWindow Window { get; }
|
||||||
|
Drawing Drawing { get; }
|
||||||
|
Audio Audio { get; }
|
||||||
|
LuaState LuaRuntime { get; set; }
|
||||||
|
Eventing.EventEmitter EventEmitter { get; }
|
||||||
|
void ConfigureServices(IServiceProvider serviceProvider);
|
||||||
|
|
||||||
|
int Width { get; set; }
|
||||||
|
int Height { get; set; }
|
||||||
|
float Scale { get; set; }
|
||||||
|
void UpdateSize(bool resize = true);
|
||||||
|
|
||||||
|
event EventHandler<EventArgs> Exiting;
|
||||||
|
void Run();
|
||||||
|
void Exit();
|
||||||
|
|
||||||
|
// Integrations
|
||||||
|
DiscordIntegration Discord { get; }
|
||||||
|
}
|
||||||
|
|
@ -25,16 +25,13 @@ namespace Capy64.Integrations;
|
||||||
public class DiscordIntegration : IComponent
|
public class DiscordIntegration : IComponent
|
||||||
{
|
{
|
||||||
public DiscordRpcClient Client { get; private set; }
|
public DiscordRpcClient Client { get; private set; }
|
||||||
public readonly bool Enabled;
|
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
|
|
||||||
public DiscordIntegration(Capy64 game)
|
public DiscordIntegration(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
_configuration = game.Configuration;
|
_configuration = configuration;
|
||||||
|
|
||||||
var discordConfig = _configuration.GetSection("Integrations:Discord");
|
var discordConfig = _configuration.GetSection("Integrations:Discord");
|
||||||
Enabled = discordConfig.GetValue("Enable", false);
|
|
||||||
|
|
||||||
Client = new(discordConfig["ApplicationId"])
|
Client = new(discordConfig["ApplicationId"])
|
||||||
{
|
{
|
||||||
Logger = new ConsoleLogger() { Level = LogLevel.Warning }
|
Logger = new ConsoleLogger() { Level = LogLevel.Warning }
|
||||||
|
|
@ -44,16 +41,15 @@ public class DiscordIntegration : IComponent
|
||||||
|
|
||||||
Capy64.Instance.Discord = this;
|
Capy64.Instance.Discord = this;
|
||||||
|
|
||||||
if (Enabled)
|
if (discordConfig.GetValue("Enable", false))
|
||||||
|
{
|
||||||
Client.Initialize();
|
Client.Initialize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
public void SetPresence(string details, string? state = null)
|
public void SetPresence(string details, string? state = null)
|
||||||
{
|
{
|
||||||
if (!Enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Client.SetPresence(new RichPresence()
|
Client.SetPresence(new RichPresence()
|
||||||
{
|
{
|
||||||
Details = details,
|
Details = details,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
using Capy64.API;
|
using Capy64.API;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -32,7 +33,7 @@ internal class PluginLoader
|
||||||
return loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path)));
|
return loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<IComponent> LoadAllPlugins(string pluginsPath)
|
public static List<IComponent> LoadAllPlugins(string pluginsPath, IServiceProvider provider)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(pluginsPath))
|
if (!Directory.Exists(pluginsPath))
|
||||||
Directory.CreateDirectory(pluginsPath);
|
Directory.CreateDirectory(pluginsPath);
|
||||||
|
|
@ -46,7 +47,7 @@ internal class PluginLoader
|
||||||
{
|
{
|
||||||
if (typeof(IComponent).IsAssignableFrom(type))
|
if (typeof(IComponent).IsAssignableFrom(type))
|
||||||
{
|
{
|
||||||
IComponent result = Activator.CreateInstance(type, Capy64.Instance) as IComponent;
|
IComponent result = ActivatorUtilities.CreateInstance(provider, type) as IComponent;
|
||||||
plugins.Add(result);
|
plugins.Add(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,34 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
using var game = new Capy64.Capy64();
|
using Capy64;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
game.Run();
|
using var game = new Capy64.Capy64();
|
||||||
|
using IHost host = Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureAppConfiguration((context, c) =>
|
||||||
|
{
|
||||||
|
var settingsPath = Path.Combine(Capy64.Capy64.AppDataPath, "settings.json");
|
||||||
|
if (!Directory.Exists(Capy64.Capy64.AppDataPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Capy64.Capy64.AppDataPath);
|
||||||
|
}
|
||||||
|
if (!File.Exists(settingsPath))
|
||||||
|
{
|
||||||
|
File.Copy("Assets/default.json", settingsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AddJsonFile("Assets/default.json", false);
|
||||||
|
c.AddJsonFile(settingsPath, false);
|
||||||
|
})
|
||||||
|
.ConfigureServices((hostContext, services) =>
|
||||||
|
{
|
||||||
|
services.AddSingleton<IGame>(game);
|
||||||
|
services.AddHostedService<Worker>();
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await host.RunAsync();
|
||||||
|
|
@ -18,6 +18,4 @@ namespace Capy64.Runtime;
|
||||||
public class Constants
|
public class Constants
|
||||||
{
|
{
|
||||||
public const int MULTRET = -1;
|
public const int MULTRET = -1;
|
||||||
public const int MINSTACK = 20;
|
|
||||||
public const int MAXLENNUM = 200;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,4 +97,17 @@ public static class Utils
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
[Obsolete("This method does not work as intended and requires more research")]
|
||||||
|
public static void PushManagedObject<T>(this Lua L, T obj)
|
||||||
|
{
|
||||||
|
var type = obj.GetType();
|
||||||
|
var members = type.GetMembers().Where(m => m.MemberType == MemberTypes.Method);
|
||||||
|
L.CreateTable(0, members.Count());
|
||||||
|
foreach (var m in members)
|
||||||
|
{
|
||||||
|
L.PushCFunction(L => (int)type.InvokeMember(m.Name, BindingFlags.InvokeMethod, null, obj, new object[] { L }));
|
||||||
|
L.SetField(-2, m.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,18 +21,18 @@ using static Capy64.Core.Audio;
|
||||||
|
|
||||||
namespace Capy64.Runtime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
public class AudioLib : IComponent
|
public class Audio : IComponent
|
||||||
{
|
{
|
||||||
private const int queueLimit = 8;
|
private const int queueLimit = 8;
|
||||||
|
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
public AudioLib(Capy64 game)
|
public Audio(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
_game.EventEmitter.OnClose += OnClose;
|
_game.EventEmitter.OnClose += OnClose;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly LuaRegister[] Library = new LuaRegister[]
|
private static readonly LuaRegister[] AudioLib = new LuaRegister[]
|
||||||
{
|
{
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
|
|
@ -85,18 +85,10 @@ public class AudioLib : IComponent
|
||||||
private static int OpenLib(IntPtr state)
|
private static int OpenLib(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
L.NewLib(Library);
|
L.NewLib(AudioLib);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert 8bit PCM to 16bit PCM
|
|
||||||
private static void PCMTo16bit(Span<byte> buffer, int i, byte value)
|
|
||||||
{
|
|
||||||
var value16bit = value / sbyte.MaxValue * short.MaxValue;
|
|
||||||
buffer[2 * i - 2] = (byte)(value16bit & 0xff);
|
|
||||||
buffer[2 * i - 1] = (byte)(value16bit >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int L_Play(IntPtr state)
|
private static int L_Play(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
@ -105,24 +97,18 @@ public class AudioLib : IComponent
|
||||||
|
|
||||||
if (L.IsString(1))
|
if (L.IsString(1))
|
||||||
{
|
{
|
||||||
var inputBuffer = L.CheckBuffer(1);
|
buffer = L.CheckBuffer(1);
|
||||||
buffer = new byte[inputBuffer.Length * 2];
|
|
||||||
var span = new Span<byte>(buffer);
|
|
||||||
for (int i = 1; i < inputBuffer.Length; i++)
|
|
||||||
{
|
|
||||||
PCMTo16bit(span, i, inputBuffer[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
L.CheckType(1, LuaType.Table);
|
L.CheckType(1, LuaType.Table);
|
||||||
var len = L.RawLen(1);
|
var len = L.RawLen(1);
|
||||||
buffer = new byte[len * 2];
|
buffer = new byte[len];
|
||||||
var span = new Span<byte>(buffer);
|
|
||||||
for (int i = 1; i <= len; i++)
|
for (int i = 1; i <= len; i++)
|
||||||
{
|
{
|
||||||
L.GetInteger(1, i);
|
L.GetInteger(1, i);
|
||||||
PCMTo16bit(span, i, (byte)L.CheckNumber(-1));
|
var value = L.CheckInteger(-1);
|
||||||
|
buffer[i - 1] = (byte)value;
|
||||||
L.Pop(1);
|
L.Pop(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -147,12 +133,7 @@ public class AudioLib : IComponent
|
||||||
L.Error(ex.Message);
|
L.Error(ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
var playTime = _game.Audio.HQChannel.GetSampleDuration(buffer.Length);
|
return 0;
|
||||||
|
|
||||||
L.PushBoolean(true);
|
|
||||||
L.PushNumber(playTime.TotalSeconds);
|
|
||||||
|
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int L_Beep(IntPtr state)
|
private static int L_Beep(IntPtr state)
|
||||||
|
|
@ -20,7 +20,7 @@ using System;
|
||||||
|
|
||||||
namespace Capy64.Runtime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
public class EventLib : IComponent
|
public class Event : IComponent
|
||||||
{
|
{
|
||||||
private const int MaxPushQueue = 64;
|
private const int MaxPushQueue = 64;
|
||||||
private static int PushQueue = 0;
|
private static int PushQueue = 0;
|
||||||
|
|
@ -29,14 +29,24 @@ public class EventLib : IComponent
|
||||||
|
|
||||||
private static bool FrozenTaskAwaiter = false;
|
private static bool FrozenTaskAwaiter = false;
|
||||||
|
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
public EventLib(Capy64 game)
|
public Event(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly LuaRegister[] Library = new LuaRegister[]
|
private static readonly LuaRegister[] EventLib = new LuaRegister[]
|
||||||
{
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
name = "pull",
|
||||||
|
function = L_Pull,
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
name = "pullRaw",
|
||||||
|
function = L_PullRaw
|
||||||
|
},
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
name = "push",
|
name = "push",
|
||||||
|
|
@ -78,10 +88,54 @@ public class EventLib : IComponent
|
||||||
private static int OpenLib(IntPtr state)
|
private static int OpenLib(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
L.NewLib(Library);
|
L.NewLib(EventLib);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int LK_Pull(IntPtr state, int status, IntPtr ctx)
|
||||||
|
{
|
||||||
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
|
if (L.ToString(1) == "interrupt")
|
||||||
|
{
|
||||||
|
L.Error("interrupt");
|
||||||
|
}
|
||||||
|
|
||||||
|
var nargs = L.GetTop();
|
||||||
|
|
||||||
|
return nargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int L_Pull(IntPtr state)
|
||||||
|
{
|
||||||
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
|
var nargs = L.GetTop();
|
||||||
|
for (int i = 1; i <= nargs; i++)
|
||||||
|
{
|
||||||
|
L.CheckString(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
L.YieldK(nargs, 0, LK_Pull);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int L_PullRaw(IntPtr state)
|
||||||
|
{
|
||||||
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
|
var nargs = L.GetTop();
|
||||||
|
for (int i = 1; i <= nargs; i++)
|
||||||
|
{
|
||||||
|
L.CheckString(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Yield(nargs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private static int L_Push(IntPtr state)
|
private static int L_Push(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
@ -138,11 +192,7 @@ public class EventLib : IComponent
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
L.CheckType(1, LuaType.Table);
|
var task = TaskMeta.CheckTask(L, false);
|
||||||
|
|
||||||
L.GetField(1, "task");
|
|
||||||
|
|
||||||
var task = TaskMeta.CheckTask(L, -1, false);
|
|
||||||
L.CheckAny(2);
|
L.CheckAny(2);
|
||||||
L.ArgumentCheck(!L.IsNil(2), 2, "value cannot be nil");
|
L.ArgumentCheck(!L.IsNil(2), 2, "value cannot be nil");
|
||||||
|
|
||||||
|
|
@ -168,11 +218,7 @@ public class EventLib : IComponent
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
L.CheckType(1, LuaType.Table);
|
var task = TaskMeta.CheckTask(L, false);
|
||||||
|
|
||||||
L.GetField(1, "task");
|
|
||||||
|
|
||||||
var task = TaskMeta.CheckTask(L, -1, false);
|
|
||||||
var error = L.CheckString(2);
|
var error = L.CheckString(2);
|
||||||
|
|
||||||
if (!task.UserTask)
|
if (!task.UserTask)
|
||||||
|
|
@ -24,11 +24,11 @@ using System.Linq;
|
||||||
|
|
||||||
namespace Capy64.Runtime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
public class FileSystemLib : IComponent
|
public class FileSystem : IComponent
|
||||||
{
|
{
|
||||||
public static string DataPath = Path.Combine(Capy64.AppDataPath, "data");
|
public static string DataPath = Path.Combine(Capy64.AppDataPath, "data");
|
||||||
|
|
||||||
public FileSystemLib()
|
public FileSystem()
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(DataPath))
|
if (!Directory.Exists(DataPath))
|
||||||
{
|
{
|
||||||
|
|
@ -37,7 +37,7 @@ public class FileSystemLib : IComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
// functions to add to the library, always end libraries with null
|
// functions to add to the library, always end libraries with null
|
||||||
private readonly LuaRegister[] Library = new LuaRegister[] {
|
private readonly LuaRegister[] FsLib = new LuaRegister[] {
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
name = "list",
|
name = "list",
|
||||||
|
|
@ -111,8 +111,6 @@ public class FileSystemLib : IComponent
|
||||||
new(), // NULL
|
new(), // NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
public FileSystemLib(Capy64 _) { }
|
|
||||||
|
|
||||||
public void LuaInit(Lua state)
|
public void LuaInit(Lua state)
|
||||||
{
|
{
|
||||||
// Add "fs" library to lua, not global (uses require())
|
// Add "fs" library to lua, not global (uses require())
|
||||||
|
|
@ -122,7 +120,7 @@ public class FileSystemLib : IComponent
|
||||||
private int Open(IntPtr state)
|
private int Open(IntPtr state)
|
||||||
{
|
{
|
||||||
var l = Lua.FromIntPtr(state);
|
var l = Lua.FromIntPtr(state);
|
||||||
l.NewLib(Library);
|
l.NewLib(FsLib);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,9 +132,6 @@ public class FileSystemLib : IComponent
|
||||||
// Get drive root (C:\ for Windows, / for *nix)
|
// Get drive root (C:\ for Windows, / for *nix)
|
||||||
var rootPath = Path.GetFullPath(Path.GetPathRoot("/") ?? "/");
|
var rootPath = Path.GetFullPath(Path.GetPathRoot("/") ?? "/");
|
||||||
|
|
||||||
var invalidPathChars = Path.GetInvalidPathChars();
|
|
||||||
path = invalidPathChars.Aggregate(path, (current, invalidChar) => current.Replace(invalidChar, ' '));
|
|
||||||
|
|
||||||
// Join path to rootPath and resolves to absolute path
|
// Join path to rootPath and resolves to absolute path
|
||||||
// Relative paths are resolved here (es. ../ and ./)
|
// Relative paths are resolved here (es. ../ and ./)
|
||||||
var absolutePath = Path.GetFullPath(path, rootPath);
|
var absolutePath = Path.GetFullPath(path, rootPath);
|
||||||
|
|
@ -431,26 +426,19 @@ public class FileSystemLib : IComponent
|
||||||
L.Error("path not found");
|
L.Error("path not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
var attr = File.GetAttributes(path);
|
||||||
|
if (attr.HasFlag(FileAttributes.Directory))
|
||||||
{
|
{
|
||||||
var attr = File.GetAttributes(path);
|
if (!recursive && Directory.GetFileSystemEntries(path).Any())
|
||||||
if (attr.HasFlag(FileAttributes.Directory))
|
|
||||||
{
|
{
|
||||||
if (!recursive && Directory.GetFileSystemEntries(path).Any())
|
L.Error("directory not empty");
|
||||||
{
|
return 0;
|
||||||
L.Error("directory not empty");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Directory.Delete(path, recursive);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
File.Delete(path);
|
|
||||||
}
|
}
|
||||||
|
Directory.Delete(path, recursive);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
else
|
||||||
{
|
{
|
||||||
return L.Error(e.Message);
|
File.Delete(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -25,16 +25,16 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Capy64.Runtime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
public class GPULib : IComponent
|
public class GPU : IComponent
|
||||||
{
|
{
|
||||||
|
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
public GPULib(Capy64 game)
|
public GPU(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly LuaRegister[] Library = new LuaRegister[] {
|
private readonly LuaRegister[] gpuLib = new LuaRegister[] {
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
name = "getSize",
|
name = "getSize",
|
||||||
|
|
@ -46,11 +46,6 @@ public class GPULib : IComponent
|
||||||
function = L_SetSize,
|
function = L_SetSize,
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
|
||||||
name = "isResizable",
|
|
||||||
function = L_IsResizable,
|
|
||||||
},
|
|
||||||
new()
|
|
||||||
{
|
{
|
||||||
name = "getPixel",
|
name = "getPixel",
|
||||||
function = L_GetPixel,
|
function = L_GetPixel,
|
||||||
|
|
@ -121,11 +116,6 @@ public class GPULib : IComponent
|
||||||
function = L_NewBuffer,
|
function = L_NewBuffer,
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
|
||||||
name = "bufferFrom",
|
|
||||||
function = L_BufferFrom,
|
|
||||||
},
|
|
||||||
new()
|
|
||||||
{
|
{
|
||||||
name = "drawBuffer",
|
name = "drawBuffer",
|
||||||
function = L_DrawBuffer,
|
function = L_DrawBuffer,
|
||||||
|
|
@ -151,12 +141,14 @@ public class GPULib : IComponent
|
||||||
public int OpenLib(IntPtr state)
|
public int OpenLib(IntPtr state)
|
||||||
{
|
{
|
||||||
var l = Lua.FromIntPtr(state);
|
var l = Lua.FromIntPtr(state);
|
||||||
l.NewLib(Library);
|
l.NewLib(gpuLib);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void GetColor(uint c, out byte r, out byte g, out byte b)
|
public static void GetColor(uint c, out byte r, out byte g, out byte b)
|
||||||
{
|
{
|
||||||
|
/*if (_game.EngineMode == EngineMode.Classic)
|
||||||
|
c = ColorPalette.GetColor(c);*/
|
||||||
Utils.UnpackRGB(c, out r, out g, out b);
|
Utils.UnpackRGB(c, out r, out g, out b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,7 +168,8 @@ public class GPULib : IComponent
|
||||||
|
|
||||||
if (_game.EngineMode == EngineMode.Classic)
|
if (_game.EngineMode == EngineMode.Classic)
|
||||||
{
|
{
|
||||||
return L.Error("Screen is not resizable");
|
L.PushBoolean(false);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var w = L.CheckInteger(1);
|
var w = L.CheckInteger(1);
|
||||||
|
|
@ -192,15 +185,6 @@ public class GPULib : IComponent
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int L_IsResizable(IntPtr state)
|
|
||||||
{
|
|
||||||
var L = Lua.FromIntPtr(state);
|
|
||||||
|
|
||||||
L.PushBoolean(_game.EngineMode != EngineMode.Classic);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int L_GetPixel(IntPtr state)
|
private static int L_GetPixel(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
@ -300,7 +284,7 @@ public class GPULib : IComponent
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var x1 = (int)L.CheckNumber(1) - 1; // do not question, please
|
var x1 = (int)L.CheckNumber(1);
|
||||||
var y1 = (int)L.CheckNumber(2);
|
var y1 = (int)L.CheckNumber(2);
|
||||||
var x2 = (int)L.CheckNumber(3);
|
var x2 = (int)L.CheckNumber(3);
|
||||||
var y2 = (int)L.CheckNumber(4);
|
var y2 = (int)L.CheckNumber(4);
|
||||||
|
|
@ -473,57 +457,6 @@ public class GPULib : IComponent
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int L_BufferFrom(IntPtr state)
|
|
||||||
{
|
|
||||||
var L = Lua.FromIntPtr(state);
|
|
||||||
|
|
||||||
L.CheckType(1, LuaType.Table);
|
|
||||||
var width = (int)L.CheckInteger(2);
|
|
||||||
var height = (int)L.CheckInteger(3);
|
|
||||||
|
|
||||||
if (width <= 0)
|
|
||||||
{
|
|
||||||
return L.ArgumentError(2, "width must be a positive integer.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (height <= 0)
|
|
||||||
{
|
|
||||||
return L.ArgumentError(3, "height must be a positive integer.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = new uint[width * height];
|
|
||||||
|
|
||||||
var tableSize = L.RawLen(1);
|
|
||||||
L.ArgumentCheck(tableSize == buffer.Length, 1, "table length does not match buffer size");
|
|
||||||
|
|
||||||
for (int i = 1; i <= tableSize; i++)
|
|
||||||
{
|
|
||||||
L.GetInteger(1, i);
|
|
||||||
var value = (uint)L.CheckInteger(-1);
|
|
||||||
L.Pop(1);
|
|
||||||
// ARGB to ABGR
|
|
||||||
value =
|
|
||||||
(value & 0xFF_00_00_00U) |
|
|
||||||
((value & 0x00_FF_00_00U) >> 16) | // move R
|
|
||||||
(value & 0x00_00_FF_00U) | // move G
|
|
||||||
((value & 0x00_00_00_FFU) << 16); // move B
|
|
||||||
|
|
||||||
buffer[i - 1] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var gpuBuffer = new GPUBufferMeta.GPUBuffer
|
|
||||||
{
|
|
||||||
Buffer = buffer,
|
|
||||||
Width = width,
|
|
||||||
Height = height,
|
|
||||||
};
|
|
||||||
|
|
||||||
ObjectManager.PushObject(L, gpuBuffer);
|
|
||||||
L.SetMetaTable(GPUBufferMeta.ObjectType);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int L_DrawBuffer(IntPtr state)
|
private static int L_DrawBuffer(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
@ -533,107 +466,13 @@ public class GPULib : IComponent
|
||||||
var x = (int)L.CheckInteger(2) - 1;
|
var x = (int)L.CheckInteger(2) - 1;
|
||||||
var y = (int)L.CheckInteger(3) - 1;
|
var y = (int)L.CheckInteger(3) - 1;
|
||||||
|
|
||||||
Rectangle? source = null;
|
|
||||||
Color color = Color.White;
|
|
||||||
float rotation = 0;
|
|
||||||
Vector2 origin = Vector2.Zero;
|
|
||||||
Vector2 scale = Vector2.One;
|
|
||||||
SpriteEffects effects = SpriteEffects.None;
|
|
||||||
|
|
||||||
if (L.IsTable(4))
|
|
||||||
{
|
|
||||||
if (L.GetField(-1, "source") == LuaType.Table)
|
|
||||||
{
|
|
||||||
int sx, sy, sw, sh;
|
|
||||||
|
|
||||||
if (L.GetInteger(-1, 1) != LuaType.Number)
|
|
||||||
L.CheckNumber(-1);
|
|
||||||
sx = (int)L.ToNumber(-1);
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetInteger(-1, 2) != LuaType.Number)
|
|
||||||
L.CheckNumber(-1);
|
|
||||||
sy = (int)L.ToNumber(-1);
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetInteger(-1, 3) != LuaType.Number)
|
|
||||||
L.CheckNumber(-1);
|
|
||||||
sw = (int)L.ToNumber(-1);
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetInteger(-1, 4) != LuaType.Number)
|
|
||||||
L.CheckNumber(-1);
|
|
||||||
sh = (int)L.ToNumber(-1);
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
source = new(sx, sy, sw, sh);
|
|
||||||
}
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetField(-1, "color") == LuaType.Number)
|
|
||||||
{
|
|
||||||
var c = (uint)L.ToNumber(-1);
|
|
||||||
GetColor(c, out var r, out var g, out var b);
|
|
||||||
color = new Color(r, g, b);
|
|
||||||
}
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetField(-1, "rotation") == LuaType.Number)
|
|
||||||
{
|
|
||||||
rotation = (float)L.ToNumber(-1);
|
|
||||||
}
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetField(-1, "origin") == LuaType.Table)
|
|
||||||
{
|
|
||||||
int ox, oy;
|
|
||||||
|
|
||||||
if (L.GetInteger(-1, 1) != LuaType.Number)
|
|
||||||
L.CheckNumber(-1);
|
|
||||||
ox = (int)L.ToNumber(-1);
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetInteger(-1, 2) != LuaType.Number)
|
|
||||||
L.CheckNumber(-1);
|
|
||||||
oy = (int)L.ToNumber(-1);
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
origin = new Vector2(ox, oy);
|
|
||||||
}
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetField(-1, "scale") == LuaType.Table)
|
|
||||||
{
|
|
||||||
float sx = 1;
|
|
||||||
float sy = 1;
|
|
||||||
|
|
||||||
if (L.GetInteger(-1, 1) == LuaType.Number)
|
|
||||||
sx = (float)L.ToNumber(-1);
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetInteger(-1, 2) == LuaType.Number)
|
|
||||||
sy = (float)L.ToNumber(-1);
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
scale = new(sx, sy);
|
|
||||||
}
|
|
||||||
L.Pop(1);
|
|
||||||
|
|
||||||
if (L.GetField(-1, "effects") == LuaType.Number)
|
|
||||||
{
|
|
||||||
var flags = L.CheckInteger(-1);
|
|
||||||
effects = (SpriteEffects)flags;
|
|
||||||
}
|
|
||||||
L.Pop(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_game.Drawing.DrawBuffer(buffer.Buffer, new()
|
_game.Drawing.DrawBuffer(buffer.Buffer, new()
|
||||||
{
|
{
|
||||||
X = x,
|
X = x,
|
||||||
Y = y,
|
Y = y,
|
||||||
Width = buffer.Width,
|
Width = buffer.Width,
|
||||||
Height = buffer.Height,
|
Height = buffer.Height,
|
||||||
}, source, color, rotation, origin, scale, effects);
|
});
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -644,7 +483,7 @@ public class GPULib : IComponent
|
||||||
|
|
||||||
var path = L.CheckString(1);
|
var path = L.CheckString(1);
|
||||||
|
|
||||||
path = FileSystemLib.Resolve(path);
|
path = FileSystem.Resolve(path);
|
||||||
|
|
||||||
if (!File.Exists(path))
|
if (!File.Exists(path))
|
||||||
{
|
{
|
||||||
|
|
@ -28,17 +28,17 @@ using System.Threading;
|
||||||
|
|
||||||
namespace Capy64.Runtime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
#nullable enable
|
#nullable enable
|
||||||
public class HTTPLib : IComponent
|
public class HTTP : IComponent
|
||||||
{
|
{
|
||||||
private static Capy64 _game = null!;
|
private static IGame _game;
|
||||||
private static HttpClient _httpClient = null!;
|
private static HttpClient _httpClient;
|
||||||
private static long _requestId;
|
private static long _requestId;
|
||||||
public static readonly HashSet<WebSocketClient.Client> WebSocketConnections = new();
|
public static readonly HashSet<WebSocketClient.Client> WebSocketConnections = new();
|
||||||
|
|
||||||
public static readonly string UserAgent = $"Capy64/{Capy64.Version}";
|
public static readonly string UserAgent = $"Capy64/{Capy64.Version}";
|
||||||
|
|
||||||
private static IConfiguration _configuration = null!;
|
private static IConfiguration _configuration;
|
||||||
private readonly LuaRegister[] Library = new LuaRegister[]
|
private readonly LuaRegister[] HttpLib = new LuaRegister[]
|
||||||
{
|
{
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
|
|
@ -57,13 +57,13 @@ public class HTTPLib : IComponent
|
||||||
},
|
},
|
||||||
new(),
|
new(),
|
||||||
};
|
};
|
||||||
public HTTPLib(Capy64 game)
|
public HTTP(IGame game, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
_httpClient = new();
|
_httpClient = new();
|
||||||
_httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent);
|
_httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent);
|
||||||
_configuration = game.Configuration;
|
_configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LuaInit(Lua L)
|
public void LuaInit(Lua L)
|
||||||
|
|
@ -75,7 +75,7 @@ public class HTTPLib : IComponent
|
||||||
private int Open(IntPtr state)
|
private int Open(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
L.NewLib(Library);
|
L.NewLib(HttpLib);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,15 +22,15 @@ using System;
|
||||||
|
|
||||||
namespace Capy64.Runtime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
public class MachineLib : IComponent
|
public class Machine : IComponent
|
||||||
{
|
{
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
public MachineLib(Capy64 game)
|
public Machine(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly LuaRegister[] Library = new LuaRegister[]
|
private static readonly LuaRegister[] MachineLib = new LuaRegister[]
|
||||||
{
|
{
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
|
|
@ -83,7 +83,7 @@ public class MachineLib : IComponent
|
||||||
private static int OpenLib(IntPtr state)
|
private static int OpenLib(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
L.NewLib(Library);
|
L.NewLib(MachineLib);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,14 +94,10 @@ public class MachineLib : IComponent
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int L_Reboot(IntPtr state)
|
private static int L_Reboot(IntPtr _)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
|
||||||
|
|
||||||
RuntimeManager.Reboot();
|
RuntimeManager.Reboot();
|
||||||
|
|
||||||
L.Yield(0);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
100
Capy64/Runtime/Libraries/TCP.cs
Normal file
100
Capy64/Runtime/Libraries/TCP.cs
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||||
|
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
using Capy64.API;
|
||||||
|
using KeraLua;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
|
public class TCP : IComponent
|
||||||
|
{
|
||||||
|
private static int Counter = 0;
|
||||||
|
private static IGame _game;
|
||||||
|
public TCP(IGame game)
|
||||||
|
{
|
||||||
|
_game = game;
|
||||||
|
Counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly LuaRegister[] TCPLib = new LuaRegister[]
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
name = "connectAsync",
|
||||||
|
function = L_Connect,
|
||||||
|
},
|
||||||
|
new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
public void LuaInit(Lua L)
|
||||||
|
{
|
||||||
|
//L.RequireF("tcp", OpenLib, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int OpenLib(nint state)
|
||||||
|
{
|
||||||
|
var L = Lua.FromIntPtr(state);
|
||||||
|
L.NewLib(TCPLib);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int L_Connect(nint state)
|
||||||
|
{
|
||||||
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
|
var host = L.CheckString(1);
|
||||||
|
var port = (int)L.CheckInteger(2);
|
||||||
|
L.ArgumentCheck(port >= 0 && port <= 0xffff, 2, "port must be in range 0-65535");
|
||||||
|
|
||||||
|
var client = new TcpClient();
|
||||||
|
var id = Counter++;
|
||||||
|
var task = client.ConnectAsync(host, port);
|
||||||
|
|
||||||
|
task.ContinueWith(t =>
|
||||||
|
{
|
||||||
|
if (client.Connected)
|
||||||
|
{
|
||||||
|
_game.LuaRuntime.QueueEvent("tcp_connect", LK =>
|
||||||
|
{
|
||||||
|
/*var handle = new FileHandle
|
||||||
|
{
|
||||||
|
Stream = client.GetStream(),
|
||||||
|
DefaultSize = client.ReceiveBufferSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
LK.PushInteger(id);
|
||||||
|
ObjectManager.PushObject(L, handle);
|
||||||
|
L.SetMetaTable("file");*/
|
||||||
|
return 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_game.LuaRuntime.QueueEvent("tcp_failure", LK =>
|
||||||
|
{
|
||||||
|
LK.PushInteger(id);
|
||||||
|
LK.PushString(t.Exception.Message);
|
||||||
|
return 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
L.PushInteger(id);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,7 @@ using static Capy64.Utils;
|
||||||
|
|
||||||
namespace Capy64.Runtime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
internal class TermLib : IComponent
|
internal class Term : IComponent
|
||||||
{
|
{
|
||||||
private struct Char
|
private struct Char
|
||||||
{
|
{
|
||||||
|
|
@ -49,11 +49,11 @@ internal class TermLib : IComponent
|
||||||
public static Color BackgroundColor { get; set; }
|
public static Color BackgroundColor { get; set; }
|
||||||
private static Char?[] CharGrid;
|
private static Char?[] CharGrid;
|
||||||
|
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
private static bool cursorState = false;
|
private static bool cursorState = false;
|
||||||
private static bool enableCursor = true;
|
private static bool enableCursor = true;
|
||||||
private static Texture2D cursorTexture;
|
private static Texture2D cursorTexture;
|
||||||
public TermLib(Capy64 game)
|
public Term(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ internal class TermLib : IComponent
|
||||||
_game.EventEmitter.OnScreenSizeChange += OnScreenSizeChange;
|
_game.EventEmitter.OnScreenSizeChange += OnScreenSizeChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly LuaRegister[] Library = new LuaRegister[]
|
private readonly LuaRegister[] TermLib = new LuaRegister[]
|
||||||
{
|
{
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
|
|
@ -99,10 +99,6 @@ internal class TermLib : IComponent
|
||||||
name = "setSize",
|
name = "setSize",
|
||||||
function = L_SetSize,
|
function = L_SetSize,
|
||||||
},
|
},
|
||||||
new() {
|
|
||||||
name = "isResizable",
|
|
||||||
function = L_IsResizable,
|
|
||||||
},
|
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
name = "getForeground",
|
name = "getForeground",
|
||||||
|
|
@ -174,7 +170,7 @@ internal class TermLib : IComponent
|
||||||
public int Open(IntPtr state)
|
public int Open(IntPtr state)
|
||||||
{
|
{
|
||||||
var l = Lua.FromIntPtr(state);
|
var l = Lua.FromIntPtr(state);
|
||||||
l.NewLib(Library);
|
l.NewLib(TermLib);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -401,7 +397,8 @@ internal class TermLib : IComponent
|
||||||
|
|
||||||
if (_game.EngineMode == EngineMode.Classic)
|
if (_game.EngineMode == EngineMode.Classic)
|
||||||
{
|
{
|
||||||
return L.Error("Terminal is not resizable");
|
L.PushBoolean(false);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var w = (int)L.CheckNumber(1);
|
var w = (int)L.CheckNumber(1);
|
||||||
|
|
@ -422,15 +419,6 @@ internal class TermLib : IComponent
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int L_IsResizable(IntPtr state)
|
|
||||||
{
|
|
||||||
var L = Lua.FromIntPtr(state);
|
|
||||||
|
|
||||||
L.PushBoolean(_game.EngineMode != EngineMode.Classic);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int L_GetForegroundColor(IntPtr state)
|
private static int L_GetForegroundColor(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
@ -21,15 +21,9 @@ using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace Capy64.Runtime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
class TimerLib : IComponent
|
class Timer : IComponent
|
||||||
{
|
{
|
||||||
public class Timer
|
private readonly LuaRegister[] TimerLib = new LuaRegister[]
|
||||||
{
|
|
||||||
public int RemainingTicks = 0;
|
|
||||||
public TaskMeta.RuntimeTask Task = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly LuaRegister[] Library = new LuaRegister[]
|
|
||||||
{
|
{
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
|
|
@ -50,54 +44,25 @@ class TimerLib : IComponent
|
||||||
new(),
|
new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
private static uint _timerId = 0;
|
private static uint _timerId = 0;
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<uint, Timer> timers = new();
|
private static readonly ConcurrentDictionary<uint, System.Timers.Timer> timers = new();
|
||||||
public TimerLib(Capy64 game)
|
public Timer(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
|
|
||||||
_game.EventEmitter.OnTick += OnTick;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTick(object sender, Eventing.Events.TickEvent e)
|
|
||||||
{
|
|
||||||
if (e.IsActiveTick)
|
|
||||||
{
|
|
||||||
foreach (var t in timers)
|
|
||||||
{
|
|
||||||
var timer = t.Value;
|
|
||||||
timer.RemainingTicks--;
|
|
||||||
if (timer.RemainingTicks <= 0)
|
|
||||||
{
|
|
||||||
if (timer.Task == null)
|
|
||||||
{
|
|
||||||
_game.LuaRuntime.QueueEvent("timer", lk =>
|
|
||||||
{
|
|
||||||
lk.PushInteger(t.Key);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
timer.Task.Fulfill(lk =>
|
|
||||||
{
|
|
||||||
lk.PushInteger(t.Key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
timers.TryRemove(t.Key, out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LuaInit(Lua state)
|
public void LuaInit(Lua state)
|
||||||
{
|
{
|
||||||
_timerId = 0;
|
_timerId = 0;
|
||||||
|
|
||||||
|
foreach (var pair in timers)
|
||||||
|
{
|
||||||
|
pair.Value.Stop();
|
||||||
|
pair.Value.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
timers.Clear();
|
timers.Clear();
|
||||||
|
|
||||||
state.RequireF("timer", Open, false);
|
state.RequireF("timer", Open, false);
|
||||||
|
|
@ -106,23 +71,36 @@ class TimerLib : IComponent
|
||||||
private int Open(IntPtr state)
|
private int Open(IntPtr state)
|
||||||
{
|
{
|
||||||
var l = Lua.FromIntPtr(state);
|
var l = Lua.FromIntPtr(state);
|
||||||
l.NewLib(Library);
|
l.NewLib(TimerLib);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static int L_StartTimer(IntPtr state)
|
private static int L_StartTimer(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var delay = L.CheckNumber(1);
|
var delay = L.CheckNumber(1);
|
||||||
|
L.ArgumentCheck(delay > 0, 1, "delay must be greater than 0");
|
||||||
|
|
||||||
var timerId = _timerId++;
|
var timerId = _timerId++;
|
||||||
|
var timer = new System.Timers.Timer
|
||||||
timers[timerId] = new Timer
|
|
||||||
{
|
{
|
||||||
RemainingTicks = (int)(delay * Capy64.Instance.TickRate)
|
AutoReset = false,
|
||||||
|
Enabled = true,
|
||||||
|
Interval = delay,
|
||||||
|
};
|
||||||
|
|
||||||
|
timers[timerId] = timer;
|
||||||
|
|
||||||
|
timer.Elapsed += (o, e) =>
|
||||||
|
{
|
||||||
|
_game.LuaRuntime.QueueEvent("timer", lk =>
|
||||||
|
{
|
||||||
|
lk.PushInteger(timerId);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
timers.TryRemove(timerId, out _);
|
||||||
};
|
};
|
||||||
|
|
||||||
L.PushInteger(timerId);
|
L.PushInteger(timerId);
|
||||||
|
|
@ -134,15 +112,27 @@ class TimerLib : IComponent
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var delay = L.CheckNumber(1);
|
var delay = L.CheckNumber(1);
|
||||||
|
L.ArgumentCheck(delay > 0, 1, "delay must be greater than 0");
|
||||||
|
|
||||||
var task = TaskMeta.Push(L, "timer");
|
var task = TaskMeta.Push(L, "timer");
|
||||||
|
|
||||||
var timerId = _timerId++;
|
var timerId = _timerId++;
|
||||||
|
var timer = new System.Timers.Timer
|
||||||
timers[timerId] = new Timer
|
|
||||||
{
|
{
|
||||||
RemainingTicks = (int)(delay * Capy64.Instance.TickRate),
|
AutoReset = false,
|
||||||
Task = task,
|
Enabled = true,
|
||||||
|
Interval = delay,
|
||||||
|
};
|
||||||
|
|
||||||
|
timers[timerId] = timer;
|
||||||
|
|
||||||
|
timer.Elapsed += (o, e) =>
|
||||||
|
{
|
||||||
|
task.Fulfill(lk =>
|
||||||
|
{
|
||||||
|
lk.PushInteger(timerId);
|
||||||
|
});
|
||||||
|
timers.TryRemove(timerId, out _);
|
||||||
};
|
};
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -60,6 +60,7 @@ public class LuaState : IDisposable
|
||||||
if (yieldTimedOut)
|
if (yieldTimedOut)
|
||||||
{
|
{
|
||||||
L.Error("no yield timeout");
|
L.Error("no yield timeout");
|
||||||
|
Console.WriteLine("tick");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,37 +25,23 @@ public class ObjectManager : IComponent
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<nint, object> _objects = new();
|
private static readonly ConcurrentDictionary<nint, object> _objects = new();
|
||||||
|
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
public ObjectManager(Capy64 game)
|
public ObjectManager(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
_game.EventEmitter.OnClose += OnClose;
|
_game.EventEmitter.OnClose += OnClose;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static nint PushObject<T>(Lua L, T obj)
|
public static void PushObject<T>(Lua L, T obj)
|
||||||
{
|
{
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
{
|
{
|
||||||
L.PushNil();
|
L.PushNil();
|
||||||
return nint.Zero;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var p = L.NewUserData(1);
|
var p = L.NewUserData(1);
|
||||||
_objects[p] = obj;
|
_objects[p] = obj;
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T GetObject<T>(nint address, bool freeGCHandle = false)
|
|
||||||
{
|
|
||||||
if (!_objects.ContainsKey(address))
|
|
||||||
return default(T);
|
|
||||||
|
|
||||||
var reference = (T)_objects[address];
|
|
||||||
|
|
||||||
if (freeGCHandle)
|
|
||||||
_objects.Remove(address, out _);
|
|
||||||
|
|
||||||
return reference;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T ToObject<T>(Lua L, int index, bool freeGCHandle = true)
|
public static T ToObject<T>(Lua L, int index, bool freeGCHandle = true)
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,7 @@
|
||||||
using Capy64.API;
|
using Capy64.API;
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using static Capy64.Runtime.Constants;
|
|
||||||
|
|
||||||
namespace Capy64.Runtime.Objects;
|
namespace Capy64.Runtime.Objects;
|
||||||
|
|
||||||
|
|
@ -88,8 +84,6 @@ public class FileHandle : IComponent
|
||||||
new(),
|
new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
public FileHandle(Capy64 _) { }
|
|
||||||
|
|
||||||
public void LuaInit(Lua L)
|
public void LuaInit(Lua L)
|
||||||
{
|
{
|
||||||
CreateMeta(L);
|
CreateMeta(L);
|
||||||
|
|
@ -139,16 +133,7 @@ public class FileHandle : IComponent
|
||||||
|
|
||||||
private static bool ReadNumber(Lua L, Stream stream)
|
private static bool ReadNumber(Lua L, Stream stream)
|
||||||
{
|
{
|
||||||
var str = ReadHelper.ReadNumber(stream);
|
return false;
|
||||||
if (L.StringToNumber(str))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
L.PushNil();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ReadLine(Lua L, Stream stream, bool chop)
|
private static bool ReadLine(Lua L, Stream stream, bool chop)
|
||||||
|
|
@ -202,66 +187,53 @@ public class FileHandle : IComponent
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return G_Read(L, stream, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int G_Read(Lua L, Stream f, int first)
|
|
||||||
{
|
|
||||||
var nargs = L.GetTop() - 1;
|
var nargs = L.GetTop() - 1;
|
||||||
int n;
|
if (nargs == 0)
|
||||||
bool success;
|
|
||||||
|
|
||||||
if (nargs == 0) // no arguments?
|
|
||||||
{
|
{
|
||||||
success = ReadLine(L, f, true);
|
L.PushString("l");
|
||||||
n = first + 1; // to return 1 result
|
nargs = 1;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// ensure stack space for all results and for auxlib's buffer
|
|
||||||
L.CheckStack(nargs + MINSTACK, "too many arguments");
|
|
||||||
success = true;
|
|
||||||
for (n = first; (nargs-- > 0) && success; n++)
|
|
||||||
{
|
|
||||||
if (L.Type(n) == LuaType.Number)
|
|
||||||
{
|
|
||||||
var l = (int)L.CheckInteger(n);
|
|
||||||
success = (l == 0) ? f.Position == f.Length : ReadChars(L, f, l);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var p = L.CheckString(n);
|
|
||||||
var mode = CheckMode(p);
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case 'n': // number
|
|
||||||
success = ReadNumber(L, f);
|
|
||||||
break;
|
|
||||||
case 'l': // line
|
|
||||||
success = ReadLine(L, f, true);
|
|
||||||
break;
|
|
||||||
case 'L': // line with end-of-line
|
|
||||||
success = ReadLine(L, f, false);
|
|
||||||
break;
|
|
||||||
case 'a': // file
|
|
||||||
ReadAll(L, f); // read entire file
|
|
||||||
success = true; // always success
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return L.ArgumentError(n, "invalid format");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (int i = 2; i <= nargs + 1; i++)
|
||||||
|
{
|
||||||
|
bool success;
|
||||||
|
if (L.Type(i) == LuaType.Number)
|
||||||
|
{
|
||||||
|
success = ReadChars(L, stream, (int)L.ToNumber(2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var p = L.CheckString(i);
|
||||||
|
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(i, "invalid format");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
L.Pop(1);
|
||||||
|
L.PushNil();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success)
|
return nargs;
|
||||||
{
|
|
||||||
L.Pop(1);
|
|
||||||
L.PushNil();
|
|
||||||
}
|
|
||||||
|
|
||||||
return n - first;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int L_Write(IntPtr state)
|
private static int L_Write(IntPtr state)
|
||||||
|
|
@ -296,6 +268,7 @@ public class FileHandle : IComponent
|
||||||
|
|
||||||
private static int L_Lines(IntPtr state)
|
private static int L_Lines(IntPtr state)
|
||||||
{
|
{
|
||||||
|
return 0;
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
var maxargn = 250;
|
var maxargn = 250;
|
||||||
|
|
||||||
|
|
@ -306,51 +279,11 @@ public class FileHandle : IComponent
|
||||||
L.PushInteger(n);
|
L.PushInteger(n);
|
||||||
L.PushBoolean(false);
|
L.PushBoolean(false);
|
||||||
L.Rotate(2, 3);
|
L.Rotate(2, 3);
|
||||||
L.PushCClosure(IO_ReadLine, 3 + n);
|
L.PushCClosure(null, 3 + n); // todo
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int IO_ReadLine(IntPtr state)
|
|
||||||
{
|
|
||||||
var L = Lua.FromIntPtr(state);
|
|
||||||
var stream = ObjectManager.ToObject<Stream>(L, Lua.UpValueIndex(1), false);
|
|
||||||
int i;
|
|
||||||
int n = (int)L.ToInteger(Lua.UpValueIndex(2));
|
|
||||||
if (stream is null)
|
|
||||||
{
|
|
||||||
return L.Error("file is already closed");
|
|
||||||
}
|
|
||||||
L.SetTop(1);
|
|
||||||
L.CheckStack(n, "too many arguments");
|
|
||||||
for (i = 1; i <= n; i++)
|
|
||||||
{
|
|
||||||
L.PushCopy(Lua.UpValueIndex(3 + i));
|
|
||||||
}
|
|
||||||
n = G_Read(L, stream, 2);
|
|
||||||
Debug.Assert(n > 0);
|
|
||||||
if (L.ToBoolean(-n))
|
|
||||||
{
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (n > 1)
|
|
||||||
{
|
|
||||||
return L.Error(L.ToString(-n + 1));
|
|
||||||
}
|
|
||||||
if (L.ToBoolean(Lua.UpValueIndex(3)))
|
|
||||||
{
|
|
||||||
L.SetTop(0);
|
|
||||||
L.PushCopy(Lua.UpValueIndex(1));
|
|
||||||
stream.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int L_Flush(IntPtr state)
|
private static int L_Flush(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
@ -419,120 +352,4 @@ public class FileHandle : IComponent
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ReadHelper
|
|
||||||
{
|
|
||||||
static bool isdigit(char c)
|
|
||||||
{
|
|
||||||
return "0123456789"
|
|
||||||
.Contains(c, StringComparison.CurrentCultureIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isxdigit(char c)
|
|
||||||
{
|
|
||||||
return "0123456789abcdef"
|
|
||||||
.Contains(c, StringComparison.CurrentCultureIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isspace(char c)
|
|
||||||
{
|
|
||||||
return new char[] {
|
|
||||||
' ',
|
|
||||||
'\n',
|
|
||||||
'\t',
|
|
||||||
'\v',
|
|
||||||
'\f',
|
|
||||||
'\r',
|
|
||||||
}.Contains(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test_eof(Lua L, Stream f)
|
|
||||||
{
|
|
||||||
L.PushString("");
|
|
||||||
return f.Position != f.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool nextc(RN rn)
|
|
||||||
{
|
|
||||||
if (rn.n >= 200) // buffer overflow?
|
|
||||||
{
|
|
||||||
rn.buff[0] = '\0'; // invalidate result
|
|
||||||
return false; // fail
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rn.buff[rn.n] = rn.c; // save current char
|
|
||||||
rn.n++;
|
|
||||||
rn.c = (char)rn.f.ReadByte(); // read next one
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test2(RN rn, string set)
|
|
||||||
{
|
|
||||||
if (rn.c == set[0] || rn.c == set[1])
|
|
||||||
{
|
|
||||||
return nextc(rn);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int readdigits(RN rn, bool hex)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
while ((hex ? isxdigit(rn.c) : isdigit(rn.c)) && nextc(rn))
|
|
||||||
count++;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// https://www.lua.org/source/5.4/liolib.c.html#read_number
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ReadNumber(Stream stream)
|
|
||||||
{
|
|
||||||
RN rn = new()
|
|
||||||
{
|
|
||||||
buff = new char[201]
|
|
||||||
};
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
bool hex = false;
|
|
||||||
string decp = "..";
|
|
||||||
rn.f = stream;
|
|
||||||
rn.n = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
rn.c = (char)rn.f.ReadByte();
|
|
||||||
} while (isspace(rn.c)); /* skip spaces */
|
|
||||||
|
|
||||||
test2(rn, "+-"); /* optional sign */
|
|
||||||
if (test2(rn, "00"))
|
|
||||||
{
|
|
||||||
if (test2(rn, "xX")) hex = true; /* numeral is hexadecimal */
|
|
||||||
else count = 1; /* count initial '0' as a valid digit */
|
|
||||||
}
|
|
||||||
count += readdigits(rn, hex); // integral part
|
|
||||||
if (test2(rn, decp)) // decimal point?
|
|
||||||
count += readdigits(rn, hex); // fractional part
|
|
||||||
if (count > 0 && test2(rn, (hex ? "pP" : "eE")))
|
|
||||||
{ /* exponent mark? */
|
|
||||||
test2(rn, "-+"); /* exponent sign */
|
|
||||||
readdigits(rn, false); /* exponent digits */
|
|
||||||
}
|
|
||||||
rn.f.Position += -1;
|
|
||||||
return new string(rn.buff);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RN
|
|
||||||
{
|
|
||||||
public Stream f;
|
|
||||||
public char c;
|
|
||||||
public int n;
|
|
||||||
public char[] buff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,8 @@ public class GPUBufferMeta : IComponent
|
||||||
new(),
|
new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
public GPUBufferMeta(Capy64 game)
|
public GPUBufferMeta(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
}
|
}
|
||||||
|
|
@ -137,9 +137,8 @@ public class GPUBufferMeta : IComponent
|
||||||
|
|
||||||
var value = buffer.Buffer[key];
|
var value = buffer.Buffer[key];
|
||||||
|
|
||||||
// ABGR to ARGB
|
// ABGR to RGB
|
||||||
value =
|
value =
|
||||||
(value & 0xFF_00_00_00U) |
|
|
||||||
((value & 0x00_00_00_FFU) << 16) | // move R
|
((value & 0x00_00_00_FFU) << 16) | // move R
|
||||||
(value & 0x00_00_FF_00U) | // move G
|
(value & 0x00_00_FF_00U) | // move G
|
||||||
((value & 0x00_FF_00_00U) >> 16); // move B
|
((value & 0x00_FF_00_00U) >> 16); // move B
|
||||||
|
|
@ -174,12 +173,13 @@ public class GPUBufferMeta : IComponent
|
||||||
var value = (uint)L.ToInteger(3);
|
var value = (uint)L.ToInteger(3);
|
||||||
value = GetColor(value);
|
value = GetColor(value);
|
||||||
|
|
||||||
// ARGB to ABGR
|
// RGB to ABGR
|
||||||
value =
|
value =
|
||||||
(value & 0xFF_00_00_00U) |
|
|
||||||
((value & 0x00_FF_00_00U) >> 16) | // move R
|
((value & 0x00_FF_00_00U) >> 16) | // move R
|
||||||
(value & 0x00_00_FF_00U) | // move G
|
(value & 0x00_00_FF_00U) | // move G
|
||||||
((value & 0x00_00_00_FFU) << 16); // move B
|
((value & 0x00_00_00_FFU) << 16) | // move B
|
||||||
|
0xFF_00_00_00U;
|
||||||
|
|
||||||
|
|
||||||
buffer.Buffer[key] = value;
|
buffer.Buffer[key] = value;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,10 @@ public class Socket : IDisposable
|
||||||
|
|
||||||
public class SocketLib : IComponent
|
public class SocketLib : IComponent
|
||||||
{
|
{
|
||||||
private static Capy64 _game = null!;
|
private static readonly IGame _game;
|
||||||
public SocketLib(Capy64 game)
|
public SocketLib(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly LuaRegister[] Methods = new LuaRegister[] {
|
private static readonly LuaRegister[] Methods = new LuaRegister[] {
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ namespace Capy64.Runtime.Objects;
|
||||||
|
|
||||||
public class TaskMeta : IComponent
|
public class TaskMeta : IComponent
|
||||||
{
|
{
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
public TaskMeta(Capy64 game)
|
public TaskMeta(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +43,6 @@ public class TaskMeta : IComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid Guid { get; set; } = Guid.NewGuid();
|
public Guid Guid { get; set; } = Guid.NewGuid();
|
||||||
public nint Pointer { get; set; } = nint.Zero;
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public TaskStatus Status { get; set; } = TaskStatus.Running;
|
public TaskStatus Status { get; set; } = TaskStatus.Running;
|
||||||
public string Error { get; private set; }
|
public string Error { get; private set; }
|
||||||
|
|
@ -80,6 +79,7 @@ public class TaskMeta : IComponent
|
||||||
DataIndex = tasks.GetTop();
|
DataIndex = tasks.GetTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_game.LuaRuntime.QueueEvent("task_finish", LK =>
|
_game.LuaRuntime.QueueEvent("task_finish", LK =>
|
||||||
{
|
{
|
||||||
LK.PushString(Guid.ToString());
|
LK.PushString(Guid.ToString());
|
||||||
|
|
@ -203,58 +203,26 @@ public class TaskMeta : IComponent
|
||||||
|
|
||||||
var task = new RuntimeTask(typeName);
|
var task = new RuntimeTask(typeName);
|
||||||
|
|
||||||
L.NewTable();
|
ObjectManager.PushObject(L, task);
|
||||||
task.Pointer = ObjectManager.PushObject(L, task);
|
|
||||||
L.SetMetaTable(ObjectType);
|
|
||||||
L.SetField(-2, "task");
|
|
||||||
|
|
||||||
L.SetMetaTable(ObjectType);
|
L.SetMetaTable(ObjectType);
|
||||||
|
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RuntimeTask ToTask(Lua L, int index = 1, bool gc = false)
|
public static RuntimeTask ToTask(Lua L, bool gc = false)
|
||||||
{
|
{
|
||||||
RuntimeTask task;
|
return ObjectManager.ToObject<RuntimeTask>(L, 1, gc);
|
||||||
if (L.Type(index) != LuaType.Table)
|
|
||||||
{
|
|
||||||
if (L.TestUserData(index, ObjectType) == nint.Zero)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
task = ObjectManager.ToObject<RuntimeTask>(L, index, gc);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
L.GetField(index, "task");
|
|
||||||
task = task = ObjectManager.ToObject<RuntimeTask>(L, -1, gc);
|
|
||||||
}
|
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RuntimeTask CheckTask(Lua L, int index = 1, bool gc = false)
|
public static RuntimeTask CheckTask(Lua L, bool gc = false)
|
||||||
{
|
{
|
||||||
RuntimeTask task;
|
var obj = ObjectManager.CheckObject<RuntimeTask>(L, 1, ObjectType, gc);
|
||||||
if (L.Type(index) != LuaType.Table)
|
if (obj is null)
|
||||||
{
|
|
||||||
task = ObjectManager.CheckObject<RuntimeTask>(L, index, ObjectType, gc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
L.GetField(index, "task");
|
|
||||||
task = ObjectManager.CheckObject<RuntimeTask>(L, -1, ObjectType, gc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task is null)
|
|
||||||
{
|
{
|
||||||
L.Error("attempt to use a closed task");
|
L.Error("attempt to use a closed task");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return task;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int FindSpot()
|
private static int FindSpot()
|
||||||
|
|
@ -287,9 +255,7 @@ public class TaskMeta : IComponent
|
||||||
|
|
||||||
private static void WaitForTask(Lua L)
|
private static void WaitForTask(Lua L)
|
||||||
{
|
{
|
||||||
L.PushString("coroutine");
|
L.PushCFunction(Libraries.Event.L_Pull);
|
||||||
L.GetTable(-1);
|
|
||||||
L.GetField(-1, "yield");
|
|
||||||
L.PushString("task_finish");
|
L.PushString("task_finish");
|
||||||
L.CallK(1, 4, 0, LK_Await);
|
L.CallK(1, 4, 0, LK_Await);
|
||||||
}
|
}
|
||||||
|
|
@ -300,7 +266,7 @@ public class TaskMeta : IComponent
|
||||||
|
|
||||||
L.Warning("Native task awaiter should be avoided", false);
|
L.Warning("Native task awaiter should be avoided", false);
|
||||||
|
|
||||||
var task = CheckTask(L, 1, false);
|
var task = CheckTask(L, false);
|
||||||
|
|
||||||
if (task.Status == TaskStatus.Succeeded)
|
if (task.Status == TaskStatus.Succeeded)
|
||||||
{
|
{
|
||||||
|
|
@ -327,9 +293,7 @@ public class TaskMeta : IComponent
|
||||||
private static int LK_Await(IntPtr state, int status, nint ctx)
|
private static int LK_Await(IntPtr state, int status, nint ctx)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
var task = CheckTask(L, false);
|
||||||
var task = CheckTask(L, 1, false);
|
|
||||||
|
|
||||||
var taskId = L.CheckString(3);
|
var taskId = L.CheckString(3);
|
||||||
|
|
||||||
if (task.Guid.ToString() != taskId)
|
if (task.Guid.ToString() != taskId)
|
||||||
|
|
@ -345,7 +309,7 @@ public class TaskMeta : IComponent
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var task = CheckTask(L, 1, false);
|
var task = CheckTask(L, false);
|
||||||
|
|
||||||
L.PushString(task.Guid.ToString());
|
L.PushString(task.Guid.ToString());
|
||||||
|
|
||||||
|
|
@ -356,7 +320,7 @@ public class TaskMeta : IComponent
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var task = CheckTask(L, 1, false);
|
var task = CheckTask(L, false);
|
||||||
|
|
||||||
L.PushString(task.Name);
|
L.PushString(task.Name);
|
||||||
|
|
||||||
|
|
@ -367,7 +331,7 @@ public class TaskMeta : IComponent
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var task = CheckTask(L, 1, false);
|
var task = CheckTask(L, false);
|
||||||
|
|
||||||
L.PushString(task.Status.ToString().ToLower());
|
L.PushString(task.Status.ToString().ToLower());
|
||||||
|
|
||||||
|
|
@ -378,7 +342,7 @@ public class TaskMeta : IComponent
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var task = CheckTask(L, 1, false);
|
var task = CheckTask(L, false);
|
||||||
|
|
||||||
if (task.Status == TaskStatus.Succeeded)
|
if (task.Status == TaskStatus.Succeeded)
|
||||||
{
|
{
|
||||||
|
|
@ -398,7 +362,7 @@ public class TaskMeta : IComponent
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var task = CheckTask(L, 1, false);
|
var task = CheckTask(L, false);
|
||||||
|
|
||||||
if (task.Status == TaskStatus.Failed)
|
if (task.Status == TaskStatus.Failed)
|
||||||
{
|
{
|
||||||
|
|
@ -416,11 +380,7 @@ public class TaskMeta : IComponent
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
L.CheckType(1, LuaType.Table);
|
var task = ToTask(L, true);
|
||||||
|
|
||||||
L.GetField(1, "task");
|
|
||||||
|
|
||||||
var task = CheckTask(L, -1, true);
|
|
||||||
if (task is null)
|
if (task is null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -438,9 +398,7 @@ public class TaskMeta : IComponent
|
||||||
private static int LM_ToString(IntPtr state)
|
private static int LM_ToString(IntPtr state)
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var task = ToTask(L);
|
var task = ToTask(L);
|
||||||
|
|
||||||
L.PushString("Task<{0}>: {1} ({2})", task?.Name, task?.Guid, task?.Status);
|
L.PushString("Task<{0}>: {1} ({2})", task?.Name, task?.Guid, task?.Status);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,6 @@ public class WebSocketClient : IComponent
|
||||||
new(),
|
new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
public WebSocketClient(Capy64 _) { }
|
|
||||||
|
|
||||||
public void LuaInit(Lua L)
|
public void LuaInit(Lua L)
|
||||||
{
|
{
|
||||||
CreateMeta(L);
|
CreateMeta(L);
|
||||||
|
|
@ -154,7 +152,7 @@ public class WebSocketClient : IComponent
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
HTTPLib.WebSocketConnections.Remove(client);
|
HTTP.WebSocketConnections.Remove(client);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,29 +25,29 @@ public class PanicScreen
|
||||||
|
|
||||||
public static void Render(string error, string details = null)
|
public static void Render(string error, string details = null)
|
||||||
{
|
{
|
||||||
TermLib.ForegroundColor = ForegroundColor;
|
Term.ForegroundColor = ForegroundColor;
|
||||||
TermLib.BackgroundColor = BackgroundColor;
|
Term.BackgroundColor = BackgroundColor;
|
||||||
TermLib.SetCursorBlink(false);
|
Term.SetCursorBlink(false);
|
||||||
TermLib.SetSize(57, 23);
|
Term.SetSize(57, 23);
|
||||||
TermLib.Clear();
|
Term.Clear();
|
||||||
|
|
||||||
var title = " Capy64 ";
|
var title = " Capy64 ";
|
||||||
var halfX = (TermLib.Width / 2) + 1;
|
var halfX = (Term.Width / 2) + 1;
|
||||||
TermLib.SetCursorPosition(halfX - (title.Length / 2), 2);
|
Term.SetCursorPosition(halfX - (title.Length / 2), 2);
|
||||||
TermLib.ForegroundColor = BackgroundColor;
|
Term.ForegroundColor = BackgroundColor;
|
||||||
TermLib.BackgroundColor = ForegroundColor;
|
Term.BackgroundColor = ForegroundColor;
|
||||||
TermLib.Write(title);
|
Term.Write(title);
|
||||||
|
|
||||||
TermLib.ForegroundColor = ForegroundColor;
|
Term.ForegroundColor = ForegroundColor;
|
||||||
TermLib.BackgroundColor = BackgroundColor;
|
Term.BackgroundColor = BackgroundColor;
|
||||||
TermLib.SetCursorPosition(1, 4);
|
Term.SetCursorPosition(1, 4);
|
||||||
Print(error + '\n');
|
Print(error + '\n');
|
||||||
|
|
||||||
if (details is not null)
|
if (details is not null)
|
||||||
{
|
{
|
||||||
Print(details);
|
Print(details);
|
||||||
}
|
}
|
||||||
TermLib.SetCursorPosition(1, 23);
|
Term.SetCursorPosition(1, 23);
|
||||||
Print("Hold CTRL + ALT + INSERT to reboot.");
|
Print("Hold CTRL + ALT + INSERT to reboot.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,12 +55,12 @@ public class PanicScreen
|
||||||
{
|
{
|
||||||
foreach (var ch in txt)
|
foreach (var ch in txt)
|
||||||
{
|
{
|
||||||
TermLib.Write(ch.ToString());
|
Term.Write(ch.ToString());
|
||||||
if (TermLib.CursorPosition.X >= TermLib.Width || ch == '\n')
|
if (Term.CursorPosition.X >= Term.Width || ch == '\n')
|
||||||
{
|
{
|
||||||
TermLib.SetCursorPosition(1, (int)TermLib.CursorPosition.Y + 1);
|
Term.SetCursorPosition(1, (int)Term.CursorPosition.Y + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TermLib.SetCursorPosition(1, (int)TermLib.CursorPosition.Y + 1);
|
Term.SetCursorPosition(1, (int)Term.CursorPosition.Y + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@ internal class RuntimeManager : IComponent
|
||||||
private static bool close = false;
|
private static bool close = false;
|
||||||
private static bool inPanic = false;
|
private static bool inPanic = false;
|
||||||
|
|
||||||
private static Capy64 _game;
|
private static IGame _game;
|
||||||
public RuntimeManager(Capy64 game)
|
public RuntimeManager(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
|
|
||||||
|
|
@ -97,8 +97,6 @@ internal class RuntimeManager : IComponent
|
||||||
{
|
{
|
||||||
_game.Discord.SetPresence("Booting up...");
|
_game.Discord.SetPresence("Booting up...");
|
||||||
|
|
||||||
InstallOS(false);
|
|
||||||
|
|
||||||
luaState = new LuaState();
|
luaState = new LuaState();
|
||||||
_game.LuaRuntime = luaState;
|
_game.LuaRuntime = luaState;
|
||||||
luaState.Init();
|
luaState.Init();
|
||||||
|
|
@ -113,18 +111,19 @@ internal class RuntimeManager : IComponent
|
||||||
|
|
||||||
emitter.Register();
|
emitter.Register();
|
||||||
|
|
||||||
LoadFirmware();
|
|
||||||
|
|
||||||
luaState.Thread.PushCFunction(L_OpenDataFolder);
|
luaState.Thread.PushCFunction(L_OpenDataFolder);
|
||||||
luaState.Thread.SetGlobal("openDataFolder");
|
luaState.Thread.SetGlobal("openDataFolder");
|
||||||
|
|
||||||
luaState.Thread.PushCFunction(L_InstallOS);
|
luaState.Thread.PushCFunction(L_ShouldInstallOS);
|
||||||
luaState.Thread.SetGlobal("installOS");
|
luaState.Thread.SetGlobal("shouldInstallOS");
|
||||||
|
|
||||||
|
luaState.Thread.PushCFunction(L_FlagInstalled);
|
||||||
|
luaState.Thread.SetGlobal("flagInstalled");
|
||||||
|
|
||||||
luaState.Thread.PushCFunction(L_Exit);
|
luaState.Thread.PushCFunction(L_Exit);
|
||||||
luaState.Thread.SetGlobal("exit");
|
luaState.Thread.SetGlobal("exit");
|
||||||
|
|
||||||
var status = luaState.Thread.LoadFile(Path.Combine(Capy64.AssetsPath, "Lua/bios.lua"));
|
var status = luaState.Thread.LoadFile("Assets/bios.lua");
|
||||||
if (status != LuaStatus.OK)
|
if (status != LuaStatus.OK)
|
||||||
{
|
{
|
||||||
throw new LuaException(luaState.Thread.ToString(-1));
|
throw new LuaException(luaState.Thread.ToString(-1));
|
||||||
|
|
@ -133,10 +132,11 @@ internal class RuntimeManager : IComponent
|
||||||
|
|
||||||
private void InitOS()
|
private void InitOS()
|
||||||
{
|
{
|
||||||
|
_game.Discord.SetPresence("On CapyOS");
|
||||||
|
|
||||||
luaState = new LuaState();
|
luaState = new LuaState();
|
||||||
_game.LuaRuntime = luaState;
|
_game.LuaRuntime = luaState;
|
||||||
luaState.Init();
|
luaState.Init();
|
||||||
CopyHostLibraries();
|
|
||||||
|
|
||||||
emitter = new(_game.EventEmitter, luaState);
|
emitter = new(_game.EventEmitter, luaState);
|
||||||
|
|
||||||
|
|
@ -148,14 +148,7 @@ internal class RuntimeManager : IComponent
|
||||||
|
|
||||||
emitter.Register();
|
emitter.Register();
|
||||||
|
|
||||||
LoadFirmware();
|
var initContent = File.ReadAllText(Path.Combine(FileSystem.DataPath, "init.lua"));
|
||||||
|
|
||||||
if (!File.Exists(Path.Combine(FileSystemLib.DataPath, "init.lua")))
|
|
||||||
{
|
|
||||||
throw new LuaException("Operating System not found\nMissing init.lua");
|
|
||||||
}
|
|
||||||
|
|
||||||
var initContent = File.ReadAllText(Path.Combine(FileSystemLib.DataPath, "init.lua"));
|
|
||||||
var status = luaState.Thread.LoadString(initContent, "=init.lua");
|
var status = luaState.Thread.LoadString(initContent, "=init.lua");
|
||||||
if (status != LuaStatus.OK)
|
if (status != LuaStatus.OK)
|
||||||
{
|
{
|
||||||
|
|
@ -163,36 +156,6 @@ internal class RuntimeManager : IComponent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyHostLibraries()
|
|
||||||
{
|
|
||||||
// table that will contain host libraries
|
|
||||||
luaState.Thread.NewTable();
|
|
||||||
|
|
||||||
luaState.Thread.GetGlobal("package");
|
|
||||||
luaState.Thread.GetField(-1, "loaded");
|
|
||||||
|
|
||||||
luaState.Thread.PushNil();
|
|
||||||
while (luaState.Thread.Next(-2))
|
|
||||||
{
|
|
||||||
var libname = luaState.Thread.ToString(-2);
|
|
||||||
luaState.Thread.SetField(1, libname);
|
|
||||||
}
|
|
||||||
|
|
||||||
luaState.Thread.Rotate(1, -1);
|
|
||||||
luaState.Thread.SetField(1, "_host");
|
|
||||||
luaState.Thread.SetTop(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadFirmware()
|
|
||||||
{
|
|
||||||
var firmwareContent = File.ReadAllText(Path.Combine(Capy64.AssetsPath, "Lua/firmware.lua"));
|
|
||||||
var errored = luaState.Thread.DoString(firmwareContent);
|
|
||||||
if (errored)
|
|
||||||
{
|
|
||||||
throw new LuaException(luaState.Thread.ToString(-1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Reset()
|
public static void Reset()
|
||||||
{
|
{
|
||||||
close = true;
|
close = true;
|
||||||
|
|
@ -210,19 +173,9 @@ internal class RuntimeManager : IComponent
|
||||||
_game.Exit();
|
_game.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void InstallOS(bool force = false)
|
|
||||||
{
|
|
||||||
var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed");
|
|
||||||
if (!File.Exists(installedFilePath) || force)
|
|
||||||
{
|
|
||||||
FileSystemLib.CopyDirectory(Path.Combine(Capy64.AssetsPath, "Lua/CapyOS"), FileSystemLib.DataPath, true, true);
|
|
||||||
File.Create(installedFilePath).Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int L_OpenDataFolder(IntPtr state)
|
private static int L_OpenDataFolder(IntPtr state)
|
||||||
{
|
{
|
||||||
var path = FileSystemLib.DataPath;
|
var path = FileSystem.DataPath;
|
||||||
switch (Environment.OSVersion.Platform)
|
switch (Environment.OSVersion.Platform)
|
||||||
{
|
{
|
||||||
case PlatformID.Win32NT:
|
case PlatformID.Win32NT:
|
||||||
|
|
@ -236,9 +189,22 @@ internal class RuntimeManager : IComponent
|
||||||
return 0;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,20 +121,6 @@ internal class Sandbox
|
||||||
L.SetTable(-3);
|
L.SetTable(-3);
|
||||||
|
|
||||||
L.Pop(1);
|
L.Pop(1);
|
||||||
|
|
||||||
// Replace debug.debug with a dummy function to avoid stalling the program
|
|
||||||
L.GetGlobal("debug");
|
|
||||||
|
|
||||||
L.PushString("debug");
|
|
||||||
L.PushCFunction(L_Dummy);
|
|
||||||
L.SetTable(-3);
|
|
||||||
|
|
||||||
L.Pop(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static int L_Dummy(IntPtr state)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static int L_Searcher(IntPtr state)
|
internal static int L_Searcher(IntPtr state)
|
||||||
|
|
@ -206,7 +192,7 @@ internal class Sandbox
|
||||||
var errorMessage = new StringBuilder();
|
var errorMessage = new StringBuilder();
|
||||||
foreach (var possiblePath in possiblePaths)
|
foreach (var possiblePath in possiblePaths)
|
||||||
{
|
{
|
||||||
var path = FileSystemLib.Resolve(possiblePath);
|
var path = FileSystem.Resolve(possiblePath);
|
||||||
var info = new FileInfo(path);
|
var info = new FileInfo(path);
|
||||||
if (!info.Exists)
|
if (!info.Exists)
|
||||||
{
|
{
|
||||||
|
|
@ -243,7 +229,7 @@ internal class Sandbox
|
||||||
bool hasMode = !L.IsNone(2);
|
bool hasMode = !L.IsNone(2);
|
||||||
bool hasEnv = !L.IsNone(3);
|
bool hasEnv = !L.IsNone(3);
|
||||||
|
|
||||||
var path = FileSystemLib.Resolve(filename);
|
var path = FileSystem.Resolve(filename);
|
||||||
|
|
||||||
var fileInfo = new FileInfo(path);
|
var fileInfo = new FileInfo(path);
|
||||||
if (!fileInfo.Exists)
|
if (!fileInfo.Exists)
|
||||||
|
|
|
||||||
73
Capy64/Worker.cs
Normal file
73
Capy64/Worker.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||||
|
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Capy64;
|
||||||
|
|
||||||
|
public class Worker : IHostedService
|
||||||
|
{
|
||||||
|
private readonly IGame _game;
|
||||||
|
private readonly IHostApplicationLifetime _appLifetime;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public Worker(IGame game, IHostApplicationLifetime appLifetime, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_game = game;
|
||||||
|
_appLifetime = appLifetime;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_appLifetime.ApplicationStarted.Register(OnStarted);
|
||||||
|
_appLifetime.ApplicationStopping.Register(OnStopping);
|
||||||
|
_appLifetime.ApplicationStopped.Register(OnStopped);
|
||||||
|
|
||||||
|
_game.Exiting += OnGameExiting;
|
||||||
|
|
||||||
|
_game.ConfigureServices(_serviceProvider);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGameExiting(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
StopAsync(new CancellationToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_appLifetime.StopApplication();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStarted()
|
||||||
|
{
|
||||||
|
_game.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStopping()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStopped()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
using Capy64.API;
|
using Capy64;
|
||||||
|
using Capy64.API;
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
|
|
||||||
namespace ExamplePlugin;
|
namespace ExamplePlugin;
|
||||||
|
|
||||||
public class MyPlugin : IComponent
|
public class MyPlugin : IComponent
|
||||||
{
|
{
|
||||||
private static Capy64.Capy64 _game;
|
private static IGame _game;
|
||||||
public MyPlugin(Capy64.Capy64 game)
|
public MyPlugin(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.8 KiB |
|
|
@ -1,11 +0,0 @@
|
||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
|
|
||||||
Name=Capy64
|
|
||||||
Comment=Lua Fantasy Computer
|
|
||||||
Categories=Game;Emulator;
|
|
||||||
|
|
||||||
Icon=me.alexdevs.Capy64
|
|
||||||
Exec=Capy64
|
|
||||||
Terminal=false
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<component type="desktop-application">
|
|
||||||
<id>me.alexdevs.Capy64</id>
|
|
||||||
<launchable type="desktop-id">me.alexdevs.Capy64.desktop</launchable>
|
|
||||||
|
|
||||||
<name>Capy64</name>
|
|
||||||
<summary>Lua Fantasy Computer</summary>
|
|
||||||
<developer_name>AlexDevs</developer_name>
|
|
||||||
<url>https://capy64.alexdevs.me/</url>
|
|
||||||
|
|
||||||
<metadata_license>MIT</metadata_license>
|
|
||||||
<project_license>Apache-2.0</project_license>
|
|
||||||
|
|
||||||
<content_rating type="oars-1.1" />
|
|
||||||
|
|
||||||
<supports>
|
|
||||||
<control>pointing</control>
|
|
||||||
<control>keyboard</control>
|
|
||||||
<control>gamepad</control>
|
|
||||||
</supports>
|
|
||||||
|
|
||||||
<description>
|
|
||||||
<p>
|
|
||||||
Capy64 is a fantasy console that runs sandbox Lua 5.4.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Create anything, from Hello Worlds to games!
|
|
||||||
</p>
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<releases>
|
|
||||||
<release version="1.1.2" date="2023-07-24" type="beta">
|
|
||||||
<url>https://github.com/Ale32bit/Capy64/releases/tag/v1.1.2-beta</url>
|
|
||||||
</release>
|
|
||||||
</releases>
|
|
||||||
|
|
||||||
</component>
|
|
||||||
19
default.nix
19
default.nix
|
|
@ -1,19 +0,0 @@
|
||||||
{ pkgs ? import <nixpkgs> { system = builtins.currentSystem; } }:
|
|
||||||
pkgs.buildDotnetModule rec {
|
|
||||||
pname = "Capy64";
|
|
||||||
version = "1.1.0-beta";
|
|
||||||
|
|
||||||
src = ./.;
|
|
||||||
|
|
||||||
projectFile = "Capy64/Capy64.csproj";
|
|
||||||
nugetDeps = ./deps.nix;
|
|
||||||
|
|
||||||
dotnet-sdk = pkgs.dotnetCorePackages.sdk_7_0;
|
|
||||||
dotnet-runtime = pkgs.dotnetCorePackages.runtime_7_0;
|
|
||||||
|
|
||||||
meta = with pkgs.lib; {
|
|
||||||
homepage = "https://github.com/Ale32bit/Capy64";
|
|
||||||
description = "Capy64";
|
|
||||||
license = with licenses; [ asl20 ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
53
deps.nix
53
deps.nix
|
|
@ -1,53 +0,0 @@
|
||||||
# This file was automatically generated by passthru.fetch-deps.
|
|
||||||
# Please dont edit it manually, your changes might get overwritten!
|
|
||||||
|
|
||||||
{ fetchNuGet }: [
|
|
||||||
(fetchNuGet { pname = "Cyotek.Drawing.BitmapFont"; version = "2.0.4"; sha256 = "04n0lq9sqfjzyvkqav6qrc8v9fb7jv1n7jk0j4r6ivfbmffzq8if"; })
|
|
||||||
(fetchNuGet { pname = "DiscordRichPresence"; version = "1.1.3.18"; sha256 = "0p4bhaggjjfd4gl06yiphqgncxgcq2bws4sjkrw0n2ldf3hgrps3"; })
|
|
||||||
(fetchNuGet { pname = "FontStashSharp.Base"; version = "1.1.8"; sha256 = "00b4x3hcldp1f6g32khhyv32z127ab6rcgmd4b88k1534vv128bp"; })
|
|
||||||
(fetchNuGet { pname = "FontStashSharp.MonoGame"; version = "1.2.8"; sha256 = "0qgp1i54zm27qxilxbpznv9s3mgf831x7xz5z3w66zcp53qgyk93"; })
|
|
||||||
(fetchNuGet { pname = "FontStashSharp.Rasterizers.StbTrueTypeSharp"; version = "1.1.8"; sha256 = "1rnnwx748378hdbfvldxzacwc710ivvlcjzm9qi2pdh8qcz4v6db"; })
|
|
||||||
(fetchNuGet { pname = "KeraLua"; version = "1.3.3"; sha256 = "1y76f6582fld1jpbkawv26344p6xwdk07xj3im25p8yf2qcw8yln"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration"; version = "7.0.0"; sha256 = "0n1grglxql9llmrsbbnlz5chx8mxrb5cpvjngm0hfyrkgzcwz90d"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.Abstractions"; version = "7.0.0"; sha256 = "1as8cygz0pagg17w22nsf6mb49lr2mcl1x8i3ad1wi8lyzygy1a3"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.Binder"; version = "7.0.3"; sha256 = "1n59jk6kqqy5f0gfx99b4j2m2clylznvxj1dwm1fjn3gmh2pi35v"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.CommandLine"; version = "7.0.0"; sha256 = "1pmgjrvwdzqrxjb24cg3fd624r64lgywbqc9symd5hyl4175pwk8"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.EnvironmentVariables"; version = "7.0.0"; sha256 = "0nhh7rnh45s39x8sjn88czg7nyfpry85pkm0g619j8b468zj8nb4"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.FileExtensions"; version = "7.0.0"; sha256 = "1fk7dcz6gfhd1k1d8ksz22rnjvj1waqjzk29ym4i3dz73rsq8j1i"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.Json"; version = "7.0.0"; sha256 = "05zjmrpp99l128wijp1fy8asskc11ls871qaqr4mjnz3gbfycxnj"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.UserSecrets"; version = "7.0.0"; sha256 = "0ks7lcyvfvr3ar36f5gp89bnnblxzic5vawppfcrvhw1ivas4mp1"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.DependencyInjection"; version = "7.0.0"; sha256 = "121zs4jp8iimgbpzm3wsglhjwkc06irg1pxy8c1zcdlsg34cfq1p"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.DependencyInjection.Abstractions"; version = "7.0.0"; sha256 = "181d7mp9307fs17lyy42f8cxnjwysddmpsalky4m0pqxcimnr6g7"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.FileProviders.Abstractions"; version = "7.0.0"; sha256 = "0ff20yklyjgyjzdyv7sybczgqhgd557m05dbwxzjznr0x41b180d"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.FileProviders.Physical"; version = "7.0.0"; sha256 = "1f1h0l47abw0spssd64qkhgd7b54pyzslyb586zp21milimcfmgv"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.FileSystemGlobbing"; version = "7.0.0"; sha256 = "1812vnkn8n0i4yr3k5azcxcfx1bbpcsmms95rdyxjfrzfksr05ai"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Hosting"; version = "7.0.1"; sha256 = "1044pm14ark2d7xiqll1cykawmp6m2f3ba9w6nfw0r3a2wfbmnxn"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Hosting.Abstractions"; version = "7.0.0"; sha256 = "1h5szfsr1dalsvdj9c18y6362853chisfns0hfpsq44hz0pr8j9q"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging"; version = "7.0.0"; sha256 = "1bqd3pqn5dacgnkq0grc17cgb2i0w8z1raw12nwm3p3zhrfcvgxf"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.Abstractions"; version = "7.0.0"; sha256 = "1gn7d18i1wfy13vrwhmdv1rmsb4vrk26kqdld4cgvh77yigj90xs"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.Configuration"; version = "7.0.0"; sha256 = "1f5fhpvzwyrwxh3g1ry027s4skmklf6mbm2w0p13h0x6fbmxcb24"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.Console"; version = "7.0.0"; sha256 = "1m8ri2m3vlv9vzk0068jkrx0vkk4sqmk1kxmn8pc3wys38d38qaf"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.Debug"; version = "7.0.0"; sha256 = "14p7hrhdd58fxdhjbyjwmlzr00vs03bmns3sf2f6alsgpvbf2h1i"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.EventLog"; version = "7.0.0"; sha256 = "0q1cgi456shngxs70ar0ibshpm5qk8whw369jrl6xdxnf1vxkkq8"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.EventSource"; version = "7.0.0"; sha256 = "11rskmrijf6xv78slm38zywj6l3wjlm017kijhan1kfg56f1kvdk"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Options"; version = "7.0.1"; sha256 = "0ghz4y4gxnf2vw8yvhz9nkw21p6q2qqwh19phkk1xwxywyilr3mq"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Options.ConfigurationExtensions"; version = "7.0.0"; sha256 = "1liyprh0zha2vgmqh92n8kkjz61zwhr7g16f0gmr297z2rg1j5pj"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Extensions.Primitives"; version = "7.0.0"; sha256 = "1b4km9fszid9vp2zb3gya5ni9fn8bq62bzaas2ck2r7gs0sdys80"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.NETCore.Platforms"; version = "2.0.0"; sha256 = "1fk2fk2639i7nzy58m9dvpdnzql4vb8yl8vr19r2fp8lmj9w2jr0"; })
|
|
||||||
(fetchNuGet { pname = "Microsoft.Win32.Registry"; version = "4.5.0"; sha256 = "1zapbz161ji8h82xiajgriq6zgzmb1f3ar517p2h63plhsq5gh2q"; })
|
|
||||||
(fetchNuGet { pname = "MonoGame.Content.Builder.Task"; version = "3.8.1.303"; sha256 = "17r9aamc3wlxnvjf8s5ndsz9p6qynyklkwhawjkxsij2cw1x1syp"; })
|
|
||||||
(fetchNuGet { pname = "MonoGame.Extended"; version = "3.8.0"; sha256 = "0xgk6z3d091wd6naqiwlhp649qxvp113w5f0nddm02mh5f63mmxw"; })
|
|
||||||
(fetchNuGet { pname = "MonoGame.Extended.Graphics"; version = "3.8.0"; sha256 = "0kj4adbhds2mvd9z1sym35ms0kr4f4b62gnpbws2whc29nixs5fi"; })
|
|
||||||
(fetchNuGet { pname = "MonoGame.Framework.DesktopGL"; version = "3.8.1.303"; sha256 = "12x7ajkh254x6l1s3i4q0j3pm5p7n95xysix19wvx3x2y6pp10nd"; })
|
|
||||||
(fetchNuGet { pname = "Newtonsoft.Json"; version = "12.0.3"; sha256 = "17dzl305d835mzign8r15vkmav2hq8l6g7942dfjpnzr17wwl89x"; })
|
|
||||||
(fetchNuGet { pname = "Newtonsoft.Json"; version = "13.0.1"; sha256 = "0fijg0w6iwap8gvzyjnndds0q4b8anwxxvik7y8vgq97dram4srb"; })
|
|
||||||
(fetchNuGet { pname = "StbImageSharp"; version = "2.27.13"; sha256 = "0zqnzgnkx3c9ycyk25jjcrl8wrg3pcgk06lxf3hvsplwxwldc8fa"; })
|
|
||||||
(fetchNuGet { pname = "StbTrueTypeSharp"; version = "1.26.11"; sha256 = "1wh2jmym3pvyw8ywrwas3qgqswc6kr4az1bxbn9vb1m3vba0qjn4"; })
|
|
||||||
(fetchNuGet { pname = "System.ComponentModel.Composition"; version = "7.0.0"; sha256 = "1gkn56gclkn6qnsvaw5fzw6qb45pa7rffxph1gyqhq7ywvmm0nc3"; })
|
|
||||||
(fetchNuGet { pname = "System.Diagnostics.DiagnosticSource"; version = "7.0.1"; sha256 = "1ajh5y33apcypz5pnvzxsx44n95ciskzpvbxk0kfg7n9li3nfs1v"; })
|
|
||||||
(fetchNuGet { pname = "System.Diagnostics.EventLog"; version = "7.0.0"; sha256 = "16p8z975dnzmncfifa9gw9n3k9ycpr2qvz7lglpghsvx0fava8k9"; })
|
|
||||||
(fetchNuGet { pname = "System.Security.AccessControl"; version = "4.5.0"; sha256 = "1wvwanz33fzzbnd2jalar0p0z3x0ba53vzx1kazlskp7pwyhlnq0"; })
|
|
||||||
(fetchNuGet { pname = "System.Security.Principal.Windows"; version = "4.5.0"; sha256 = "0rmj89wsl5yzwh0kqjgx45vzf694v9p92r4x4q6yxldk1cv1hi86"; })
|
|
||||||
(fetchNuGet { pname = "System.Text.Encodings.Web"; version = "7.0.0"; sha256 = "1151hbyrcf8kyg1jz8k9awpbic98lwz9x129rg7zk1wrs6vjlpxl"; })
|
|
||||||
(fetchNuGet { pname = "System.Text.Json"; version = "7.0.0"; sha256 = "0scb0lp7wbgcinaa4kqiqs7b8i5nx4ppfad81138jiwd1sl37pyp"; })
|
|
||||||
]
|
|
||||||
Loading…
Add table
Reference in a new issue