1
0
mirror of https://github.com/20kdc/OC-KittenOS.git synced 2024-11-16 23:48:05 +11:00

Compare commits

..

No commits in common. "master" and "neo-r4" have entirely different histories.

123 changed files with 2800 additions and 5867 deletions

11
.gitignore vendored
View File

@ -1,8 +1,9 @@
# This is released into the public domain.
# No warranty is provided, implied or otherwise.
# leaving in preSH.tar.gz for anyone who's interested
# in how NOT to do compression
code.tar
code/data/app-claw/*
work.tar
work
work/
@ -19,10 +20,6 @@ repobuild/*/*
repobuild/*/*/
repobuild/*/*/*
laboratory/ocemu.cfg
laboratory/bios.lua
laboratory/data
laboratory/log
laboratory/*/
laboratory/*/*
laboratory/*/*/
@ -33,9 +30,7 @@ laboratory/*/*/*/*
inst.lua
# Available as the respective release
inst-gold.lua
# Compression stuff
inst/iSymTab
# internal
com2/code.tar.bd
upldr.sh
upldr-dev.sh
upldr-gold.sh

75
NOTE-TO-MS.asc Normal file
View File

@ -0,0 +1,75 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
- -- To Microsoft.
- -- You deserve to get yourselves investigated by Trading Standards.
- -- I don't mean that in a "and then everything comes out okay" way,
- -- I mean that as in a "you deserve to be legitimately threatened with being shutdown" way.
- -- See that MultiMC issue, #2223, about the Windows 10 thing?
- -- That's the beginnings of what you've caused, and don't you dare deny the intent.
- -- In what world is swapping around the names of two products, both in
- -- active sales no less, to get one to be chosen over the other by an
- -- uninformed buyer *not illegal*?
- -- To anyone else, I say this.
- -- If you're affected by this, just pirate the Java edition.
- -- You have been affected by what I can say without contradiction is a campaign of lies.
- -- Deliberate or otherwise, but history would point in favour of a calculated attack.
- -- Take what is frankly rightfully yours.
- -- Oh, Microsoft, did you not like that? I don't care, and it is plainly obvious why I shouldn't care.
- -- To the Mojang team - the *real* Mojang team (Anyone working on Bedrock is exempt) - I pity you,
- -- but I'm afraid I have to recommend this course of action.
- -- There is no other way. I'm sure you already know what Microsoft is like first-hand.
- -- I'm sure you've read the reports and you know Microsoft will lobby their way
- -- past anything I could even suggest to any politician, if it even got that far.
- -- I'm sure you can already see your presumed redundancies coming, and I doubt I hasten it.
- -- And frankly, preventing Microsoft from abusing Minecraft as a lock-in tactic in whatever way I can
- -- takes priority.
- -- To those who doubted what I said to them when I said it to them,
- -- to those who doubt me now, know this. It's happening, it's real,
- -- there is proof with basic searches.
- -- To those who are ignorant, and yet scared to let anyone leave them,
- -- THOSE WITHOUT WINDOWS 10 ON THEIR DESKTOPS WON'T BE ABLE TO JOIN YOU
- -- ON MC IF THIS GOES AS THEY LIKELY INTEND.
- -- You may doubt my "likely", but remember this:
- -- We thought they would do this long ago, when Microsoft bought Mojang.
- -- It took time, yes. But it started to happen and when it did, everything started moving into place.
- -- The "Better Together" (unless on a non-Windows 10 desktop) update was simply the end of a journey.
- -- When I look back, the guesses - mine and others - as to what they will do next have been delayed,
- -- but they are occurring, along with new horrors that I did not expect,
- -- like the commercialization of that offered by mods for free here on the "Java Edition".
- -- You can argue all you like about how they put work into that stuff,
- -- fact is, Bedrock is a downgrade.
- -- You could argue about performance, but - Mods fix the performance issues,
- -- and they aren't exactly hard to install. Download Forge installer. Run Forge installer.
- -- Copy one file. Done.
- -- You cannot argue about playerbase - people are left behind, unable to go to Bedrock.
- -- Even if you claim it's their fault for using Windows 10, how much have you somehow missed
- -- about Windows 10's various nastiness? There's a laptop basically unusable for anything real-time here
- -- on Windows 10 because of an update that screwed it over, and no, a reset did not fix it. Nothing did.
- -- Except moving it to another OS, but of course, that wouldn't work so well if Bedrock was a concern.
- -- Moddability has been, and probably always will be, destroyed on Bedrock.
- -- Sure, you'll eventually get some more and more access,
- -- but there's a reason the way Minecraft modding went
- -- lead to the inventive and amazing mods out there,
- -- and it certainly wasn't a bunch of "behavior packs"!
- -- To those who wonder what the hell this is doing here,
- -- I don't care. Feel free to remove the file from your local machines,
- -- but if you send a PR asking for its removal, I *will* reject it instantly.
- -- -20kdc
-----BEGIN PGP SIGNATURE-----
iQEzBAEBCAAdFiEE4HrK8U+8321iGlEUALn5S4V/2fkFAlrafToACgkQALn5S4V/
2fkW/wf/afn8PjeCYyNOWNhGmELNO5dL3eWaBb86KAiDZXMYPmCifDiXOOmYeq2t
3cqcDXujOQJN7PE8RBzrVSIm2kjLtQXoFuhww6A9p3LuyXdPT0WykMroPM5xYrrH
7c6VtaWkXngHn/Z/aLgIDsveW/LiHqG5a8N98XdPivlRgfsBPo5+/wv84gcnAGzL
vJURI/GMEuFj+Bg6Vw0fiiYF4DAVKSdSb830bW4flFTwXBHOL8SiUbEDBX1W5nj1
KbnIhnXdy/rGbjsVUlpnDIyL+IdTyRwLyxYpjvHqpdb0RjALCw+b6uzQ6feOUb2/
RsDIy5cRGY4k2sSP+yFByvNR34HphA==
=uPTk
-----END PGP SIGNATURE-----

View File

@ -1,51 +1,3 @@
# ATTENTION! WARNING! ETCETERA!!!
AS OF 11TH JULY 2022, ALL SUPPORT AND MAINTENANCE ON THIS REPOSITORY HAS BEEN DROPPED.
Microsoft finally turned off whatever mechanism PolyMC was using to let people play multiplayer on Mojang accounts.
Use UltimMC. Use something like https://modrinth.com/mod/easyauth . Host your servers this way.
Microsoft have proven they are willing to remove a bought product off your hands if you don't agree to their new rules about it.
They have proven that "buying" something off of them means nothing and anything you've "bought" from Mojang means nothing.
*They have stolen what they have been paid money for. Therefore they don't deserve to have anything bought off them at all.*
And before any of you go "just migrate", look at the warning signs as to the ban systems.
Maybe watch FitMC's video: https://www.youtube.com/watch?v=rdoFUhd0EkI
Or maybe read the migration horror stories:
+ https://www.reddit.com/r/xbox/comments/sscoa7/does_anyone_have_an_idea_why_was_i_locked_out_of/
+ https://news.ycombinator.com/item?id=31551846
+ https://answers.microsoft.com/en-us/xbox/forum/all/im-locked-out-of-minecraft-after-migrating-my/9f1f64cb-6a2d-4cd2-abb8-0010a6bc6f31
+ https://www.reddit.com/r/Minecraft/comments/we8asn/why_microsoft_ever_since_i_was_forced_to_migrate/
+ even rekrap1 got bitten by this: https://www.youtube.com/watch?v=rzPYH98TTMM
.
.
.
.
.
.
.
.
.
.
. OLD README BELOW
# KittenOS NEO
As per usual, no warranty, not my responsibility if this breaks, or if you somehow try to run it on an actual (non-OpenComputers) computer.
@ -84,16 +36,12 @@ It would be really nice if, if I have screwed up, that you tell me how.
Preferably with a solution that fits the technological constraints.
Licensing in this project is rather fluid,
but everything that is not in `repository/` is unconditionally under the following license:
but everything in code/ is unconditionally under the following license:
Copyright (C) 2018-2021 by KittenOS NEO contributors
This is released into the public domain.
No warranty is provided, implied or otherwise.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
This will be referred to as "BSD0".
This will be referred to as "Public Domain".
It should be considered equivalent to CC0, and this is the intent,
but it is smaller, which is somewhat important when optimizing for size.

87
bonecrunch.lua Normal file
View File

@ -0,0 +1,87 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- This program tries to crunch down the installer a bit further.
-- Specific target in mind, it has no support for string escapes.
-- It also does this:
for i = 1, 3 do
print(io.read())
end
local sequences = {
{"\n", " "},
{" ", " "},
{" #", "#"},
{"# ", "#"},
{" ,", ","},
{", ", ","},
{" (", "("},
{"( ", "("},
{" )", ")"},
{") ", ")"},
{" <", "<"},
{"< ", "<"},
{" >", ">"},
{"> ", ">"},
{" *", "*"},
{"* ", "*"},
{" ~", "~"},
{"~ ", "~"},
{" /", "/"},
{"/ ", "/"},
{" %", "%"},
{"% ", "%"},
{" =", "="},
{"= ", "="},
{" -", "-"},
{"- ", "-"},
{" +", "+"},
{"+ ", "+"},
{".. ", ".."},
{" ..", ".."},
{"\"\" ", "\"\""},
{"=0 t", "=0t"},
{">0 t", ">0t"},
{">1 t", ">1t"},
{"=1 w", "=1w"},
{"=380 l", "=380l"},
{"=127 t", "=127t"},
{"=128 t", "=128t"},
{">255 t", ">255t"},
{"=512 t", "=512t"}
}
local function pass(buffer)
local ob = ""
local smode = false
while #buffer > 0 do
if not smode then
local ds = true
while ds do
ds = false
for _, v in ipairs(sequences) do
if buffer:sub(1, #(v[1])) == v[1] then
buffer = v[2] .. buffer:sub(#(v[1]) + 1)
ds = true
end
end
end
end
local ch = buffer:sub(1, 1)
buffer = buffer:sub(2)
ob = ob .. ch
if ch == "\"" then
smode = not smode
end
end
return ob
end
local op = io.read("*a")
while true do
local np = pass(op)
if np == op then
io.write(np)
return
end
op = np
end

View File

@ -1,75 +0,0 @@
# Claw2 Formats
## .c2l format
The .c2l format is the server package list for Claw2.
In an exception to the rule, this file only exists on the server.
It is used solely in the main package list panel.
It is a file made up of lines.
Each line contains a package name, followed by a dot, followed by the package version.
## .V.c2p format
The .V.c2p (where V is the version) format is the entire contents of the package view panel,
as text, with newlines, in UTF-8.
This is used when a package is selected in Claw2.
## .c2x format
The .c2x format is the actual installation script for the package.
It is executed by svc-claw-worker.
It's loaded in all-at-once, then it's gmatched
with the pattern [^\n]+.
A line starting with "?" represents a dependency.
A line starting & ending with "/" represents a directory creation.
And a line starting with "+" represents a file.
Package metadata is not implied.
Thus, a valid .c2x is:
```
?neo
/apps/
+apps/app-carrot.0.c2p
+apps/app-carrot.c2x
```
## Claw2 Architecture
app-claw is a very dumb client, but the only thing that'll bother
to parse a .c2l (because it has package list/search),
and the only thing that cares about version numbers.
The purpose of it is to provide an older-CLAW-style GUI.
It *may* take an argument, in which case a package panel is opened,
otherwise the main search panel is opened.
When it wants to do anything, it shuts itself down, running svc-claw-worker.
svc-app-claw-worker does all package consistency & such work.
It can only be run from app-claw, and runs app-claw after it's done.
It takes 5 arguments:
1. The target filesystem proxy.
2. The target package name. This package is viewed in app-claw after completion.
3. The source to download files from.
If nil, the package is being deleted.
Otherwise, can either be a proxy or a string.
Proxy means it's a filesystem,
string means it's an internet base.
4. Checked flag
5. primary inet card proxy, if any

View File

@ -1,38 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- CLAW local.lua converter. Expects to be run from outermost folder.
local target = ...
local serial = loadfile("code/libs/serial.lua")()
for k, v in pairs(serial.deserialize(io.read("*a"))) do
print(k .. "." .. v.v .. ".c2p")
print(k .. ".c2x")
local f2 = io.open(target .. k .. "." .. v.v .. ".c2p", "wb")
f2:write(k .. "\n")
f2:write(v.desc .. "\n")
f2:write("v" .. v.v .. " deps " .. table.concat(v.deps, ", "))
f2:close()
f2 = io.open(target .. k .. ".c2x", "wb")
for _, vx in ipairs(v.deps) do
f2:write("?" .. vx .. "\n")
end
for _, vx in ipairs(v.dirs) do
f2:write("/" .. vx .. "\n")
end
for _, vx in ipairs(v.files) do
f2:write("+" .. vx .. "\n")
end
f2:write("/data\n")
f2:write("/data/app-claw\n")
f2:write("+data/app-claw/" .. k .. ".c2x\n")
f2:write("+data/app-claw/" .. k .. "." .. v.v .. ".c2p\n")
f2:close()
end

View File

@ -1,328 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- local.lua : CLAW Repository Metadata
-- Authors: 20kdc
return {
["app-eeprog"] = {
desc = "Example program: EEPROM programmer / copier",
v = 1,
deps = {
"neo",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-eeprog.lua",
"docs/repoauthors/app-eeprog"
},
},
["neo-docs"] = {
desc = "KittenOS NEO system documentation",
v = 10,
deps = {
"zzz-license-kosneo-bsd0"
},
dirs = {
"docs",
"docs/repoauthors"
},
files = {
"docs/an-intro",
"docs/kn-intro",
"docs/kn-refer",
"docs/kn-sched",
"docs/kn-perms",
"docs/us-perms",
"docs/us-nxapp",
"docs/us-setti",
"docs/us-evrst",
"docs/us-clawf",
"docs/us-termi",
"docs/ul-seria",
"docs/ul-fwrap",
"docs/ul-event",
"docs/ul-fmttx",
"docs/ul-neoux",
"docs/ul-brail",
"docs/ul-bmp__",
"docs/ul-linee",
"docs/gp-pedan",
"docs/repoauthors/neo-docs"
},
},
["app-nbox2018"] = {
desc = "NBOX2018 and NPRT2018, a 3D-printing toolbox",
v = 1,
deps = {
"neo",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-nbox2018.lua",
"apps/app-nprt2018.lua",
"docs/repoauthors/app-nbox2018"
},
},
["app-allmem"] = {
desc = "Near-reproducible memory usage figures",
v = 1,
deps = {
"neo",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-allmem.lua",
"docs/repoauthors/app-allmem"
},
},
["app-telnet"] = {
desc = "TELNET client",
v = 1,
deps = {
"neo",
"svc-t",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-telnet.lua",
"docs/repoauthors/app-telnet"
},
},
["svc-ghostie"] = {
desc = "Application that schedules a scare after a random time to test svc autostart",
v = 1,
deps = {
"neo",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/svc-ghostie.lua",
"apps/app-ghostcall.lua",
"docs/repoauthors/svc-ghostie"
},
},
["app-metamachine"] = {
desc = "Virtual machine",
v = 5,
deps = {
"neo",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"libs",
"docs",
"docs/repoauthors",
"data",
"data/app-metamachine"
},
files = {
"apps/app-metamachine.lua",
"libs/metamachine-vgpu.lua",
"libs/metamachine-vfs.lua",
"docs/repoauthors/app-metamachine",
"data/app-metamachine/confboot.lua",
"data/app-metamachine/lucaboot.lua"
},
},
["app-pclogix-upload"] = {
desc = "paste.pc-logix.com text uploader",
v = 1,
deps = {
"neo",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-pclogix-upload.lua",
"docs/repoauthors/app-pclogix-upload"
},
},
["app-rsctrl"] = {
desc = "Redstone control",
v = 1,
deps = {
"neo",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-rsctrl.lua",
"docs/repoauthors/app-rsctrl"
},
},
["app-nbcompose"] = {
desc = "Music player/composer using the NBS format",
v = 2,
deps = {
"neo",
"lib-knbs",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-nbcompose.lua",
"docs/repoauthors/app-nbcompose"
},
},
["app-tapedeck"] = {
desc = "Computronics Tape Drive interface",
v = 3,
deps = {
"neo",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-tapedeck.lua",
"docs/repoauthors/app-tapedeck"
},
},
["app-launchbar"] = {
desc = "Application launcher bar",
v = 0,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-launchbar.lua",
"docs/repoauthors/app-launchbar"
},
},
["app-slaunch"] = {
desc = "Searching launcher",
v = 0,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-slaunch.lua",
"docs/repoauthors/app-slaunch"
},
},
-- libraries
["lib-knbs"] = {
desc = "NBS reader/writer library",
v = 2,
deps = {
"zzz-license-kosneo-bsd0"
},
dirs = {
"libs",
"docs",
"docs/repoauthors"
},
files = {
"libs/knbs.lua",
"docs/repoauthors/lib-knbs"
},
},
["svc-virtudev"] = {
desc = "a clone of vcomponent",
v = 2,
deps = {
"neo",
"zzz-license-kosneo-bsd0"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/svc-virtudev.lua",
"apps/app-vdrslamp.lua",
"docs/us-virtu",
"docs/repoauthors/svc-virtudev"
},
},
-- licenses (MUST BE IMMUTABLE)
["zzz-license-pd"] = {
desc = "license file 'Public Domain'",
v = 0,
deps = {
},
dirs = {
"docs",
"docs/licensing",
"docs/repoauthors"
},
files = {
"docs/licensing/Public Domain",
"docs/repoauthors/zzz-license-pd"
},
},
["zzz-license-kosneo-bsd0"] = {
desc = "license file 'KittenOS NEO BSD0'",
v = 0,
deps = {
},
dirs = {
"docs",
"docs/licensing",
"docs/repoauthors"
},
files = {
"docs/licensing/KittenOS NEO BSD0",
"docs/repoauthors/zzz-license-kosneo-bsd0"
},
}
}

17
clawmerge.lua Normal file
View File

@ -0,0 +1,17 @@
local merges = {...}
neo = {
wrapMeta = function (x)
return x
end
}
local serial = loadfile("code/libs/serial.lua")()
local repo = {}
for _, v in ipairs(merges) do
local f = io.open(v, "rb")
local fd = f:read("*a")
f:close()
for k, v in pairs(serial.deserialize(fd)) do
repo[k] = v
end
end
io.write(serial.serialize(repo))

View File

@ -1,78 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- app-batmon: Still not batman.
-- Port of the original 'batmon.lua' from KittenOS Legacy.
local window = neo.requireAccess("x.neo.pub.window", "window")(10, 2)
-- OCE/s, OCE at last check, uptime of last timer set, uptime of last check
local lastChange, lastValue, lastTimer, lpTimer = 0
local usage = {
"[####]:",
"[###:]:",
"[### ]:",
"[##: ]:",
"[## ]:",
"[#: ]:",
"[# ]:",
"[: ]:",
"[ ]:",
"WARNING"
}
local function getText(y)
if y == 2 then
if not lastChange then
return "Wait..."
end
local ind = "Dc. "
local wc = lastChange
local wv = os.energy()
if wc > 0 then
wc = -wc
wv = os.maxEnergy() - wv
ind = "Ch. "
end
local m = math.floor((wv / -wc) / 60)
return ind .. m .. "m"
end
local dec = os.energy() / os.maxEnergy()
-- dec is from 0 to 1.
local potential = math.floor(dec * #usage)
if potential < 0 then potential = 1 end
if potential >= #usage then potential = #usage - 1 end
return usage[#usage - potential]
end
local function update()
local nv = os.energy()
if lastValue then
lastChange = (nv - lastValue) / (os.uptime() - lpTimer)
end
lpTimer = os.uptime()
lastValue = nv
lastTimer = os.uptime() + 10
if lastChange then
if lastChange > 10 then
lastTimer = lastTimer - 9
end
end
neo.scheduleTimer(lastTimer)
window.setSize(10, 2)
end
update()
while true do
local ev, a, b, c = coroutine.yield()
if ev == "x.neo.pub.window" then
if b == "close" then
return
elseif b == "line" then
local tx = getText(c):sub(1, 10)
window.span(1, c, tx .. (" "):rep(10 - #tx), 0xFFFFFF, 0)
end
elseif ev == "k.timer" then
update()
end
end

View File

@ -1,84 +1,148 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-claw: Package manager.
local ldrPkg, _, tgtPkg = ...
-- libs & such
local event = require("event")(neo)
local neoux = require("neoux")(event, neo)
local neoux, err = require("neoux")
if not neoux then error(err) end
neoux = neoux(event, neo)
local claw = require("app-claw-core")()
local clawcsi = require("app-claw-csi")
local source = "http://20kdc.duckdns.org/neo/"
local disks = neo.requireAccess("c.filesystem", "searching disks for packages")
local primaryDisk = disks.primary
local primaryINet = neo.requestAccess("c.internet")
if primaryINet then primaryINet = primaryINet.list()() end
--
local function readFile(src, url, ocb)
local buf = ""
local function cb(data)
if not data then
ocb(buf)
local function yielder()
-- slightly dangerous, but what can we do?
pcall(event.sleepTo, os.uptime() + 0.05)
end
local function download(url, cb)
if not primaryINet then return nil, "no internet" end
local req, err = primaryINet.request(source .. url)
if not req then
cb(nil)
return nil, "dlR/" .. tostring(err)
end
-- OpenComputers#535
req.finishConnect()
while true do
local n, n2 = req.read(neo.readBufSize)
local o, r = cb(n)
if not o then
req.close()
return nil, r
end
if not n then
req.close()
if n2 then
return nil, n2
else
break
end
else
buf = buf .. data
buf = buf:gsub("[^\n]*\n", function (t)
ocb(t:sub(1, -2))
return ""
end)
if n == "" then
yielder()
end
end
end
if type(src) == "string" then
assert(primaryINet, "no internet")
local req, err = primaryINet.request(src .. url)
assert(req, err)
-- OpenComputers#535
req.finishConnect()
while true do
local n, n2 = req.read(neo.readBufSize)
cb(n)
if not n then
req.close()
if n2 then
error(n2)
else
cb(nil)
break
end
else
if n == "" then
-- slightly dangerous, but what can we do?
pcall(event.sleepTo, os.uptime() + 0.05)
end
end
return true
end
local function fsSrc(disk)
return function (url, cb)
local h, e = disk.open(url, "rb")
if not h then cb(nil) return nil, tostring(e) end
local c = ""
while c do
c = disk.read(h, neo.readBufSize)
local o, r = cb(c)
if not o then return nil, r end
end
else
if url == "data/app-claw/local.c2l" then
for _, v in ipairs(src.list("data/app-claw/")) do
ocb(v)
end
return
end
local h, e = src.open(url, "rb")
assert(h, e)
repeat
local c = src.read(h, neo.readBufSize)
cb(c)
until not c
src.close(h)
disk.close(h)
return true
end
end
-- Sources
local function fsDst(disk)
return {function (url)
local h, e = disk.open(url, "wb")
if not h then return nil, tostring(e) end
return function (d)
local ok, r = true
if d then
ok, r = disk.write(h, d)
else
disk.close(h)
end
if not ok then return nil, tostring(r) end
return true
end
end, disk.makeDirectory, disk.exists, disk.isDirectory, disk.remove, disk.rename}
end
local function checked(...)
local res, res2, err = pcall(...)
if not res then
neoux.startDialog(tostring(res2), "error!", true)
elseif not res2 then
neoux.startDialog(tostring(err), "failed!", true)
else
return res2
end
end
-- Beginning Of The App (well, the actual one)
local genCurrent, genPrimary, genPackage, primaryWindow
local running = true
-- primary
local primarySearchTx = ""
local primaryPage = 1
local primaryList = {}
local primaryNextMinus = false
-- package
local packageLock = nil
local packageId = "FIXME"
local function describe(pkg)
local weHave = claw.getInfo(pkg, "local")
local theyHave = claw.getInfo(pkg, "local")
local someoneHas = claw.getInfo(pkg, nil, true)
if weHave then
if theyHave.v > weHave.v then
return pkg .. " [v" .. weHave.v .. "!]"
end
if someoneHas.v < weHave.v then
return pkg .. " (v" .. weHave.v .. ") R<"
end
return pkg .. " (v" .. weHave.v .. ")"
end
return pkg
end
local function primaryWindowRegenCore()
local gen, gens = genCurrent()
return 25, 12, "claw", neoux.tcwindow(25, 12, gen, function (w)
w.close()
running = false
end, 0xFF8F00, 0, gens)
end
local function primaryWindowRegen()
primaryWindow.reset(primaryWindowRegenCore())
end
local sources = {}
local sourceList = {}
-- Use all non-primary filesystems
local disks = neo.requireAccess("c.filesystem", "searching disks for packages")
local primaryDisk = disks.primary
for pass = 1, 3 do
for v in disks.list() do
local nam = nil
@ -90,8 +154,11 @@ for pass = 1, 3 do
nam = v.address
end
if nam then
sources[nam] = v
table.insert(sourceList, nam)
local ok, r = clawcsi(claw, nam, fsSrc(v), (not v.isReadOnly()) and fsDst(v))
if not ok and nam == "local" then
claw.unlock()
error(r)
end
end
end
end
@ -100,105 +167,19 @@ end
disks = nil
if primaryINet then
sources["inet"] = "http://20kdc.duckdns.org/neo/"
table.insert(sourceList, "inet")
checked(clawcsi, claw, "inet", download)
end
-- List scanning for package window
clawcsi = nil
local function scanList(content)
local lst = {}
local lst2 = {}
for k, v in pairs(sources) do
local ok, err = pcall(readFile, v, "data/app-claw/local.c2l", function (l)
if l:sub(-4) == ".c2p" then
local lt, ltv = l:sub(1, -5)
ltv = lt:match("%.[0-9]+$")
if ltv and l:find(content, 1, true) then
lt = lt:sub(1, -(#ltv + 1))
lst2[lt] = true
end
end
end)
if (not ok) and ((k == "inet") or (k == "local")) then
neoux.startDialog(tostring(err), k)
end
end
for k, v in pairs(lst2) do
table.insert(lst, k)
end
table.sort(lst)
return lst
end
-- Beginning Of The App (well, the actual one)
local genCurrent, genPrimary, genPackage, primaryWindow
local running = true
-- primary
local primarySearchTx = ""
local primaryPage = 1
local primaryList = scanList("")
local primaryNextMinus = false
-- package
local packageLock = nil
local packageId = "FIXME"
local function describe(pkgs)
local lowestV, highestV, myV = {}, {}, {}
for pk, pv in ipairs(pkgs) do
lowestV[pk] = math.huge
highestV[pk] = -math.huge
end
for k, v in pairs(sources) do
pcall(readFile, v, "data/app-claw/local.c2l", function (l)
local lp = l:match("%.[0-9]+%.c2p$")
for pk, pkg in ipairs(pkgs) do
if lp and l:sub(1, -(#lp + 1)) == pkg then
local v = tonumber(lp:sub(2, -5))
if k == "local" then
myV[pk] = v
end
lowestV[pk] = math.min(lowestV[pk], v)
highestV[pk] = math.max(highestV[pk], v)
end
end
end)
end
for pk, pkg in ipairs(pkgs) do
if lowestV[pk] == math.huge then
pkgs[pk] = pkg .. " (ERR)"
elseif myV[pk] then
if highestV[pk] > myV[pk] then
pkgs[pk] = pkg .. " [v" .. myV[pk] .. "!]"
elseif lowestV[pk] < myV[pk] then
pkgs[pk] = pkg .. " (v" .. myV[pk] .. ") R<"
else
pkgs[pk] = pkg .. " (v" .. myV[pk] .. ")"
end
end
end
end
local function primaryWindowRegenCore()
local gen, gens = genCurrent()
return 25, 14, "claw", neoux.tcwindow(25, 14, gen, function (w)
w.close()
running = false
end, 0xFF8F00, 0, gens)
end
local function primaryWindowRegen()
primaryWindow.reset(primaryWindowRegenCore())
end
primaryList = claw.getList()
-- Sections
function genPrimary()
local minus = (primaryNextMinus and 3) or nil
primaryNextMinus = false
local pgs = 12
local pgs = 10
local pages = math.ceil(#primaryList / pgs)
local elems = {
neoux.tcbutton(23, 1, "+", function (w)
@ -217,22 +198,14 @@ function genPrimary()
end)
}
local base = (primaryPage - 1) * pgs
local pkgs = {}
for i = 1, pgs do
local ent = primaryList[base + i]
if ent then
pkgs[i] = ent
end
end
describe(pkgs)
for i = 1, pgs do
local ent = primaryList[base + i]
if ent then
local enttx = pkgs[i]
local enttx = describe(ent)
table.insert(elems, neoux.tcbutton(1, i + 1, unicode.safeTextFormat(enttx), function (w)
-- FREE UP MEMORY NOW
elems = {}
w.reset(25, 14, "claw", function (ev)
w.reset(25, 12, "claw", function (ev)
if ev == "close" then
w.close()
running = false
@ -244,13 +217,22 @@ function genPrimary()
end))
end
end
table.insert(elems, neoux.tcfield(1, 14, 16, function (s)
table.insert(elems, neoux.tcfield(1, 12, 16, function (s)
if s then primarySearchTx = s end
return primarySearchTx
end))
table.insert(elems, neoux.tcbutton(17, 14, "Search!", function (w)
table.insert(elems, neoux.tcbutton(17, 12, "Search!", function (w)
local n = {}
for _, v in ipairs(claw.getList()) do
for i = 1, #v do
if v:sub(i, i + #primarySearchTx - 1) == primarySearchTx then
table.insert(n, v)
break
end
end
end
primaryPage = 1
primaryList = scanList(primarySearchTx)
primaryList = n
primaryWindowRegen()
end))
return elems, minus
@ -263,38 +245,49 @@ local function packageGetBB(src, lclI, srcI, srcW)
if srcI and srcW then
table.insert(buttons, {
"Del",
function (w)
w.close()
running = false
neo.executeAsync("svc-app-claw-worker", sources[src], packageId, nil, src == "local", primaryINet)
function ()
if packageLock then return end
packageLock = ""
checked(claw.remove, src, packageId, true)
packageLock = nil
primaryWindowRegen()
end
})
end
if srcI and ((not lclI) or (lclI < srcI)) then
if srcI and ((not lclI) or (lclI.v < srcI.v)) then
table.insert(buttons, {
"Get",
function (w)
w.close()
running = false
neo.executeAsync("svc-app-claw-worker", sources["local"], packageId, sources[src], true, primaryINet)
function ()
if packageLock then return end
packageLock = "installing from " .. src
primaryWindowRegen()
checked(claw.installTo, "local", packageId, src, true, yielder)
packageLock = nil
primaryWindowRegen()
end
})
end
if srcW and lclI and not srcI then
table.insert(buttons, {
"All",
function (w)
w.close()
running = false
neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], true, primaryINet)
function ()
if packageLock then return end
packageLock = "storing w/ dependencies at " .. src
primaryWindowRegen()
checked(claw.installTo, src, packageId, "local", true, yielder)
packageLock = nil
primaryWindowRegen()
end
})
table.insert(buttons, {
"Put",
function (w)
w.close()
running = false
neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], false, primaryINet)
function ()
if packageLock then return end
packageLock = "storing at " .. src
primaryWindowRegen()
checked(claw.installTo, src, packageId, "local", false, yielder)
packageLock = nil
primaryWindowRegen()
end
})
end
@ -313,50 +306,26 @@ function genPackage()
-- inet v21 <pull>
-- dir v22 <pull> <push>
-- crockett <push>
local sourceVers = {}
local c2pSrc = "local"
local c2pVer = -1
for k, v in pairs(sources) do
local ok, err = pcall(readFile, v, "data/app-claw/local.c2l", function (l)
local lp = l:match("%.[0-9]+%.c2p$")
if lp and l:sub(1, -(#lp + 1)) == packageId then
sourceVers[k] = tonumber(lp:sub(2, -5))
if c2pVer < sourceVers[k] then
c2pSrc = k
c2pVer = sourceVers[k]
end
end
end)
end
if sourceVers["local"] then
c2pSrc = "local"
c2pVer = sourceVers["local"]
end
local text = ""
local ok = pcall(readFile, sources[c2pSrc], "data/app-claw/" .. packageId .. "." .. c2pVer .. ".c2p", function (l)
text = text .. l .. "\n"
end)
if not ok then
text = packageId .. "\nUnable to read v" .. c2pVer .. " c2p from: " .. c2pSrc
end
local info = claw.getInfo(packageId)
local infoL = claw.getInfo(packageId, "local")
local elems = {
neoux.tcrawview(1, 1, neoux.fmtText(unicode.safeTextFormat(text), 25)),
neoux.tcrawview(1, 1, neoux.fmtText(unicode.safeTextFormat(packageId .. "\n" .. info.desc .. "\nv" .. info.v .. " deps " .. table.concat(info.deps, ", ")), 25)),
neoux.tcbutton(20, 1, "Back", function ()
if packageLock then return end
genCurrent = genPrimary
primaryWindowRegen()
end)
}
for k, v in ipairs(sourceList) do
local row = 14 + k - #sourceList
for k, v in ipairs(claw.sourceList) do
local lI = claw.getInfo(packageId, v[1])
local row = 12 + k - #(claw.sourceList)
local pfx = " "
if sourceVers[v] then
pfx = "v" .. string.format("%04i", sourceVers[v]) .. " "
if lI then
pfx = "v" .. string.format("%04i", lI.v) .. " "
end
table.insert(elems, neoux.tcrawview(1, row, {neoux.pad(pfx .. v, 14, false, true)}))
table.insert(elems, neoux.tcrawview(1, row, {neoux.pad(pfx .. v[1], 14, false, true)}))
local col = 26
local srcW = type(sources[v]) ~= "string"
for _, bv in ipairs(packageGetBB(v, sourceVers["local"], sourceVers[v], srcW)) do
for _, bv in ipairs(packageGetBB(v[1], infoL, lI, v[2])) do
local b = neoux.tcbutton(col, row, bv[1], bv[2])
col = col - b.w
b.x = col
@ -368,14 +337,10 @@ end
--
if ldrPkg == "svc-app-claw-worker" and tgtPkg then
packageId = tgtPkg
genCurrent = genPackage
else
genCurrent = genPrimary
end
genCurrent = genPrimary
primaryWindow = neoux.create(primaryWindowRegenCore())
while running do
event.pull()
end
claw.unlock()

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-control: Settings changer
local settings = neo.requireAccess("x.neo.sys.manage", "management")
@ -230,7 +226,7 @@ window = neoux.create(currentGen())
while running do
local src, id, k, v = event.pull()
if src == "x.neo.sys.manage" then
if id == "set_setting" and currentGen ~= logGen then
if id == "set_setting" then
window.reset(currentGen())
end
end

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
local event = require("event")(neo)
local neoux = require("neoux")(event, neo)
@ -24,14 +20,12 @@ local busy = false
local regenCore
local function regenLabeller(set, get, wd)
local tx = get()
return wd, 2, nil, neoux.tcwindow(wd, 1, {
neoux.tcfield(1, 1, wd, function (nt)
if nt then
tx = nt
set(nt)
end
return tx
return get()
end)
}, function (w)
busy = false
@ -45,7 +39,7 @@ function regenCore()
for v in eeprom.list() do
local lbl = unicode.safeTextFormat(v.getLabel())
table.insert(elems, neoux.tcrawview(1, l, {
require("fmttext").pad(v.address:sub(1, 8) .. " " .. lbl, 25, false, true)
v.address:sub(1, 8) .. " " .. lbl
}))
table.insert(elems, neoux.tcbutton(1, l + 1, "get", function (window)
if busy then return end

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-fm: dummy app to start FM
neo.requestAccess("x.neo.pub.base").showFileDialogAsync(nil)

View File

@ -1,15 +1,12 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
local event = require("event")(neo)
local neoux = require("neoux")(event, neo)
local braille = require("braille")
local bmp = require("bmp")
local file = neoux.fileDialog(false)
local icecap = neo.requireAccess("x.neo.pub.base", "loadimg")
local file = icecap.open("/logo.bmp", false)
local header = file.read(bmp.headerMinSzBMP)
local palette = ""
@ -64,6 +61,9 @@ lcWidth = bitmap.dsSpan
local running = true
local function decodeRGB(rgb, igp, col)
if igp and bitmap.bpp > 24 then
rgb = math.floor(rgb / 256)
end
local r, g, b = math.floor(rgb / 65536) % 256, math.floor(rgb / 256) % 256, rgb % 256
-- the new KittenOS NEO logo is 'sensitive' to dithering, so disable it
if not col then
@ -82,8 +82,6 @@ local fp = neoux.tcwindow(bW, bH, {
braille.new(1, 1, bW, bH, {
selectable = true,
get = function (window, x, y, bg, fg, selected, colour)
if x > bitmap.width then return 0, 0, 0 end
if y > bitmap.height then return 0, 0, 0 end
if bitmap.ignoresPalette then
return decodeRGB(bitmap.getPixel(x - 1, y - 1, 0), true, colour)
end
@ -97,9 +95,9 @@ end, 0xFFFFFF, 0)
neoux.create(bW, bH, nil, function (w, t, r, ...)
if t == "focus" then
if r and not bitmap.ignoresPalette then
if r then
local pal = {}
for i = 0, math.min(15, bitmap.paletteCol - 1) do
for i = 0, 15 do
pal[i + 1] = bitmap.getPalette(i)
end
w.recommendPalette(pal)

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-launcher: The launcher
local event = require("event")(neo)

View File

@ -1,168 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
local _, _, termId = ...
local ok = pcall(function ()
assert(string.sub(termId, 1, 12) == "x.neo.pub.t/")
end)
local termClose
if not ok then
termId = nil
assert(neo.executeAsync("svc-t", function (res)
termId = res.access
termClose = res.close
neo.scheduleTimer(0)
end, "luashell"))
while not termId do
coroutine.yield()
end
end
TERM = neo.requireAccess(termId, "terminal")
-- Using event makes it easier for stuff
-- within the shell to not spectacularly explode.
event = require("event")(neo)
local alive = true
event.listen("k.procdie", function (_, _, pid)
if pid == TERM.pid then
alive = false
end
end)
TERM.write([[
KittenOS NEO Shell Usage Notes
Prefixing = is an alias for 'return '.
io.read(): Reads a line.
print: 'print with table dumping' impl.
TERM: Your terminal. (see us-termi doc.)
os.execute(): launch terminal apps!
tries '*', 'sys-t-*', 'svc-t-*', 'app-*'
example: os.execute("luashell")
os.exit(): quit the shell
=listCmdApps(): -t- (terminal) apps
event: useful for setting up listeners
without breaking shell functionality
]])
function listCmdApps()
local apps = {}
for _, v in ipairs(neo.listApps()) do
if v:sub(4, 6) == "-t-" then
table.insert(apps, v)
end
end
return apps
end
local function vPrint(slike, ...)
local s = {...}
if #s > 1 then
for i = 1, #s do
if i ~= 1 then TERM.write("\t") end
vPrint(slike, s[i])
end
elseif slike and type(s[1]) == "string" then
TERM.write("\"" .. s[1] .. "\"")
elseif type(s[1]) ~= "table" then
TERM.write(tostring(s[1]))
else
TERM.write("{")
for k, v in pairs(s[1]) do
TERM.write("[")
vPrint(true, k)
TERM.write("] = ")
vPrint(true, v)
TERM.write(", ")
end
TERM.write("}")
end
end
print = function (...)
vPrint(false, ...)
TERM.write("\r\n")
end
local ioBuffer = ""
io = {
read = function ()
while alive do
local pos = ioBuffer:find("\n")
if pos then
local line = ioBuffer:sub(1, pos):gsub("\r", "")
ioBuffer = ioBuffer:sub(pos + 1)
return line
end
local e = {event.pull()}
if e[1] == TERM.id then
if e[2] == "data" then
ioBuffer = ioBuffer .. e[3]
end
end
end
end,
write = function (s) TERM.write(s) end
}
local originalOS = os
os = setmetatable({}, {
__index = originalOS
})
function os.exit()
alive = false
end
function os.execute(x, ...)
local subPid = neo.executeAsync(x, TERM.id, ...)
if not subPid then
subPid = neo.executeAsync("sys-t-" .. x, TERM.id, ...)
end
if not subPid then
subPid = neo.executeAsync("svc-t-" .. x, TERM.id, ...)
end
if not subPid then
subPid = neo.executeAsync("app-" .. x, TERM.id, ...)
end
if not subPid then
error("cannot find " .. x)
end
while true do
local e = {event.pull()}
if e[1] == "k.procdie" then
if e[3] == subPid then
return
end
end
end
end
while alive do
TERM.write("> ")
local code = io.read()
if code then
local ok, err = pcall(function ()
if code:sub(1, 1) == "=" then
code = "return " .. code:sub(2)
end
print(assert(load(code))())
end)
if not ok then
TERM.write(tostring(err) .. "\r\n")
end
end
end
if termClose then
termClose()
end

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-taskmgr: Task manager
-- a-hello : simple test program for Everest.

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 'neolithic': Text Editor
-- This was textedit (femto) from KittenOS 'ported' to NEO.
@ -15,7 +11,7 @@ local lines = {
"F5, F6, ^←: Copy, Paste, Delete Line",
-- These two are meant to replace similar functionality in GNU Nano
-- (which I consider the best console text editor out there - Neolithic is an *imitation* and a poor one at that),
-- except fixing a UI flaw by instead adding a visible way to reset the append flag,
-- except fixing a UI flaw by instead making J responsible for resetting the append flag,
-- so the user can more or less arbitrarily mash together lines
"F7: Reset 'append' flag for Cut Lines",
"F8: Cut Line(s)",
@ -34,17 +30,33 @@ local clipsrc = neo.requireAccess("x.neo.pub.globals", "clipboard")
local windows = neo.requireAccess("x.neo.pub.window", "windows")
local files = neo.requireAccess("x.neo.pub.base", "files").showFileDialogAsync
local lineEdit = require("lineedit")
local cursorX = 1
local cursorY = math.ceil(#lines / 2)
local ctrlFlag, appendFlag
local cFlash = true
local ctrlFlag = false
local dialogLock = false
local appendFlag = false
local sW, sH = 37, #lines + 2
local window = windows(sW, sH)
local filedialog = nil
local flush
local function splitCur()
local s = lines[cursorY]
local st = unicode.sub(s, 1, cursorX - 1)
local en = unicode.sub(s, cursorX)
return st, en
end
local function clampCursorX()
local s = lines[cursorY]
if unicode.len(s) < (cursorX - 1) then
cursorX = unicode.len(s) + 1
return true
end
return false
end
local cbs = {}
local function fileDialog(writing, callback)
@ -128,14 +140,31 @@ local function getline(y)
-- rX is difficult!
local rX = 1
local Xthold = math.max(1, math.floor(sW / 2) - 1)
local cLine, cursorXP = unicode.safeTextFormat(lines[cursorY], cursorX)
local _, cursorXP = unicode.safeTextFormat(lines[cursorY], cursorX)
rX = (math.max(0, math.floor(cursorXP / Xthold) - 1) * Xthold) + 1
local line = lines[rY]
if not line then
return ("¬"):rep(sW)
end
line = unicode.safeTextFormat(line)
return lineEdit.draw(sW, line, rY == cursorY and cursorXP, rX)
-- <alter RX here by 1 if needed>
local tl = unicode.sub(line, rX, rX + sW - 1)
cursorXP = (cursorXP - rX) + 1
if cFlash then
if rY == cursorY then
if cursorXP >= 1 then
if cursorXP <= sW then
local start = unicode.sub(tl, 1, cursorXP - 1)
local endx = unicode.sub(tl, cursorXP + 1)
tl = start .. "_" .. endx
end
end
end
end
while unicode.len(tl) < sW do
tl = tl .. " "
end
return tl
end
local function delLine()
local contents = lines[cursorY]
@ -151,7 +180,22 @@ local function delLine()
end
return contents
end
local function key(ks, kc, down)
-- add a single character
local function putLetter(ch)
if ch == "\r" then
local a, b = splitCur()
lines[cursorY] = a
table.insert(lines, cursorY + 1, b)
cursorY = cursorY + 1
cursorX = 1
return
end
local a, b = splitCur()
a = a .. ch
lines[cursorY] = a .. b
cursorX = unicode.len(a) + 1
end
local function key(ka, kc, down)
if dialogLock then
return false
end
@ -169,23 +213,23 @@ local function key(ks, kc, down)
sH = 1
end
sW, sH = window.setSize(sW, sH)
return false
elseif kc == 208 then -- Down
end
if kc == 208 then -- Down
sH = sH + 1
sW, sH = window.setSize(sW, sH)
return false
elseif kc == 203 then -- Left
end
if kc == 203 then -- Left
sW = sW - 1
if sW == 0 then
sW = 1
end
sW, sH = window.setSize(sW, sH)
return false
elseif kc == 205 then -- Right
end
if kc == 205 then -- Right
sW = sW + 1
sW, sH = window.setSize(sW, sH)
return false
elseif kc == 14 then -- ^Backspace
end
if kc == 14 then -- ^Backspace
delLine()
return true
end
@ -201,9 +245,10 @@ local function key(ks, kc, down)
if cursorY < 1 then
cursorY = 1
end
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
clampCursorX()
return true
elseif kc == 208 or kc == 209 then -- Go down one - go down page
end
if kc == 208 or kc == 209 then -- Go down one - go down page
local moveAmount = 1
if kc == 209 then
moveAmount = math.floor(sH / 2)
@ -212,7 +257,39 @@ local function key(ks, kc, down)
if cursorY > #lines then
cursorY = #lines
end
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
clampCursorX()
return true
end
if kc == 203 then
if cursorX > 1 then
cursorX = cursorX - 1
else
if cursorY > 1 then
cursorY = cursorY - 1
cursorX = unicode.len(lines[cursorY]) + 1
else
return false
end
end
return true
end
if kc == 205 then
cursorX = cursorX + 1
if clampCursorX() then
if cursorY < #lines then
cursorY = cursorY + 1
cursorX = 1
end
end
return true
end
-- Extra Non-Control Keys
if kc == 199 then
cursorX = 1
return true
end
if kc == 207 then
cursorX = unicode.len(lines[cursorY]) + 1
return true
end
-- Major Actions
@ -221,16 +298,17 @@ local function key(ks, kc, down)
cursorX = 1
cursorY = 1
return true
elseif kc == 61 then -- F3
end
if kc == 61 then -- F3
startLoad()
return false
elseif kc == 62 then -- F4
end
if kc == 62 then -- F4
startSave()
return false
elseif kc == 63 then -- F5
end
if kc == 63 then -- F5
clipsrc.setSetting("clipboard", lines[cursorY])
return false
elseif kc == 64 then -- F6
end
if kc == 64 then -- F6
local tx = clipsrc.getSetting("clipboard") or ""
local txi = tx:find("\n")
local nt = {}
@ -244,10 +322,11 @@ local function key(ks, kc, down)
table.insert(lines, cursorY, v)
end
return true
elseif kc == 65 then -- F7
end
if kc == 65 then -- F7
appendFlag = false
return false
elseif kc == 66 then -- F8
end
if kc == 66 then -- F8
if appendFlag then
local base = clipsrc.getSetting("clipboard")
clipsrc.setSetting("clipboard", base .. "\n" .. delLine())
@ -258,37 +337,29 @@ local function key(ks, kc, down)
return true
end
end
-- LEL Keys
local lT, lC, lX = lineEdit.key(ks, kc, lines[cursorY], cursorX)
if lT then
lines[cursorY] = lT
-- Letters
if ka == 8 or kc == 211 then
if cursorX == 1 then
if cursorY == 1 then
return false
end
local l = table.remove(lines, cursorY)
cursorY = cursorY - 1
cursorX = unicode.len(lines[cursorY]) + 1
lines[cursorY] = lines[cursorY] .. l
else
local a, b = splitCur()
a = unicode.sub(a, 1, unicode.len(a) - 1)
lines[cursorY] = a.. b
cursorX = cursorX - 1
end
return true
end
if lC then
cursorX = lC
if ka ~= 0 then
putLetter(unicode.char(ka))
return true
end
if lX == "l<" and cursorY > 1 then
cursorY = cursorY - 1
cursorX = unicode.len(lines[cursorY]) + 1
elseif lX == "l>" and cursorY < #lines then
cursorY = cursorY + 1
cursorX = 1
elseif lX == "w<" and cursorY ~= 1 then
local l = table.remove(lines, cursorY)
cursorY = cursorY - 1
cursorX = unicode.len(lines[cursorY]) + 1
lines[cursorY] = lines[cursorY] .. l
elseif lX == "w>" and cursorY ~= #lines then
local l = table.remove(lines, cursorY)
cursorX = unicode.len(l) + 1
lines[cursorY] = l .. lines[cursorY]
elseif lX == "nl" then
local line = lines[cursorY]
lines[cursorY] = unicode.sub(line, 1, cursorX - 1)
table.insert(lines, cursorY + 1, unicode.sub(line, cursorX))
cursorX = 1
cursorY = cursorY + 1
end
return true
return false
end
flush = function ()
@ -297,9 +368,16 @@ flush = function ()
end
end
neo.scheduleTimer(os.uptime() + 0.5)
while true do
local e = {coroutine.yield()}
if e[1] == "x.neo.pub.window" then
if e[1] == "k.timer" then
cFlash = not cFlash
local csY = math.ceil(sH / 2)
window.span(1, csY, getline(csY), 0xFFFFFF, 0)
neo.scheduleTimer(os.uptime() + 0.5)
elseif e[1] == "x.neo.pub.window" then
if e[2] == window.id then
if e[3] == "line" then
window.span(1, e[4], getline(e[4]), 0xFFFFFF, 0)
@ -310,10 +388,10 @@ while true do
local csY = math.ceil(sH / 2)
local nY = math.max(1, math.min(#lines, (math.floor(e[5]) - csY) + cursorY))
cursorY = nY
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
clampCursorX()
flush()
elseif e[3] == "key" then
if key(e[4] ~= 0 and unicode.char(e[4]), e[5], e[6]) then
if key(e[4], e[5], e[6]) then
flush()
end
elseif e[3] == "focus" then
@ -328,7 +406,7 @@ while true do
if c == "\n" then
c = "\r"
end
key(c, 0, true)
putLetter(c)
end
end
flush()

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
local event = require("event")(neo)
local neoux = require("neoux")(event, neo)

View File

@ -1,159 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- svc-app-claw-worker: Who stays stays. Who goes goes.
local callerPkg, _, destProx, packageId, downloadSrc, checked, primaryINet = ...
if callerPkg ~= "app-claw" then error("Internal process for app-claw's bidding.") end
-- This 'mutex' remains active as long as the process does.
neo.requireAccess("r.svc.app-claw-worker", "CLAW mutex")
local function wrapLines(ocb)
local buf = ""
return function (data)
if not data then
ocb(buf)
else
buf = buf .. data
buf = buf:gsub("[^\n]*\n", function (t)
ocb(t:sub(1, -2))
return ""
end)
end
end
end
local function download(url, cb, src)
if type(src) == "string" then
assert(primaryINet, "no internet")
local req, err = primaryINet.request(src .. url)
assert(req, err)
-- OpenComputers#535
req.finishConnect()
while true do
local n, n2 = req.read(neo.readBufSize)
cb(n)
if not n then
req.close()
if n2 then
error(n2)
else
cb(nil)
break
end
else
if n == "" then
neo.scheduleTimer(os.uptime() + 0.05)
while true do
local res = coroutine.yield()
if res == "k.timer" then break end
end
end
end
end
else
local h, e = src.open(url, "rb")
assert(h, e)
repeat
local c = src.read(h, neo.readBufSize)
cb(c)
until not c
src.close(h)
end
end
local opInstall, opRemove
function opInstall(packageId, checked)
local gback = {} -- the ultimate strategy
local gdir = {}
local preinstall = {}
download("data/app-claw/" .. packageId .. ".c2x", wrapLines(function (l)
if l:sub(1, 1) == "?" and checked then
preinstall[l:sub(2)] = true
elseif l:sub(1, 1) == "+" then
table.insert(gback, l:sub(2))
elseif l:sub(1, 1) == "/" then
table.insert(gdir, l:sub(2))
end
end), downloadSrc)
for _, v in ipairs(gdir) do
destProx.makeDirectory(v)
assert(destProx.isDirectory(v), "unable to create dir " .. v)
end
gdir = nil
for k, _ in pairs(preinstall) do
if not destProx.exists("data/app-claw/" .. k .. ".c2x") then
opInstall(k, true)
end
end
preinstall = nil
for _, v in ipairs(gback) do
local f = destProx.open(v .. ".C2T", "wb")
assert(f, "unable to create download file")
local ok, e = pcall(download, v, function (b)
assert(destProx.write(f, b or ""), "unable to save data")
end, downloadSrc)
destProx.close(f)
if not ok then error(e) end
end
-- CRITICAL SECTION --
if destProx.exists("data/app-claw/" .. packageId .. ".c2x") then
opRemove(packageId, false)
end
for _, v in ipairs(gback) do
if destProx.exists(v) then
for _, v in ipairs(gback) do
destProx.remove(v .. ".C2T")
end
error("file conflict: " .. v)
end
end
for _, v in ipairs(gback) do
destProx.rename(v .. ".C2T", v)
end
end
function opRemove(packageId, checked)
if checked then
local dependents = {}
for _, pidf in ipairs(destProx.list("data/app-claw/")) do
if pidf:sub(-4) == ".c2x" then
local pid = pidf:sub(1, -5)
download("data/app-claw/" .. pidf, wrapLines(function (l)
if l == "?" .. packageId then
table.insert(dependents, pid)
end
end), destProx)
end
end
assert(not dependents[1], "Cannot remove " .. packageId .. ", required by:\n" .. table.concat(dependents, ", "))
end
local rmSchedule = {}
download("data/app-claw/" .. packageId .. ".c2x", wrapLines(function (l)
if l:sub(1, 1) == "+" then
table.insert(rmSchedule, l:sub(2))
end
end), destProx)
for _, v in ipairs(rmSchedule) do
destProx.remove(v)
end
end
local ok, e
if downloadSrc then
ok, e = pcall(opInstall, packageId, checked)
else
ok, e = pcall(opRemove, packageId, checked)
end
destProx = nil
downloadSrc = nil
primaryINet = nil
neo.executeAsync("app-claw", packageId)
if not ok then
error(e)
end

View File

@ -1,495 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- svc-t.lua : terminal
local _, _, retTbl, title = ...
assert(retTbl, "need to alert creator")
if title ~= nil then
assert(type(title) == "string", "title must be string")
end
local function rW()
return string.format("%04x", math.random(0, 65535))
end
local id = "neo.pub.t/" .. rW() .. rW() .. rW() .. rW()
local closeNow = false
-- Terminus Registration State --
local tReg = neo.requireAccess("r." .. id, "registration")
local sendSigs = {}
-- Display State --
-- unicode.safeTextFormat'd lines.
-- The size of this must not go below 1.
local console = {}
-- This must not go below 3.
local conW = 40
local conCX, conCY = 1, 1
local conSCX, conSCY = 1, 1
-- Performance
local consoleShown = {}
local conCYShown
for i = 1, 14 do
console[i] = (" "):rep(conW)
end
-- Line Editing State --
-- Nil if line editing is off.
-- In this case, the console height
-- must be adjusted accordingly.
local leText = ""
-- These are NOT nil'd out,
-- particularly not the history buffer.
local leCX = 1
local leHistory = {
-- Size = history buffer size
"", "", "", ""
}
local function cycleHistoryUp()
local backupFirst = leHistory[1]
for i = 1, #leHistory - 1 do
leHistory[i] = leHistory[i + 1]
end
leHistory[#leHistory] = backupFirst
end
local function cycleHistoryDown()
local backup = leHistory[1]
for i = 2, #leHistory do
backup, leHistory[i] = leHistory[i], backup
end
leHistory[1] = backup
end
-- Window --
local window = neo.requireAccess("x.neo.pub.window", "window")(conW, #console + 1, title)
-- Core Terminal Functions --
local function setSize(w, h)
conW = w
for i = 1, #console do
consoleShown[i] = nil
end
while #console < h do
table.insert(console, "")
end
while #console > h do
table.remove(console, 1)
end
for i = 1, #console do
console[i] = unicode.sub(console[i], 1, w) .. (" "):rep(w - unicode.len(console[i]))
end
if leText then
window.setSize(w, h + 1)
else
window.setSize(w, h)
end
conCX, conCY = 1, h
end
local function setLineEditing(state)
if state and not leText then
leText = ""
leCX = 1
setSize(conW, #console)
elseif leText and not state then
leText = nil
setSize(conW, #console)
end
end
local function draw(i)
if console[i] then
window.span(1, i, console[i], 0, 0xFFFFFF)
if i == conCY and not leText then
window.span(conCX, i, unicode.sub(console[i], conCX, conCX), 0xFFFFFF, 0)
end
elseif leText then
window.span(1, i, require("lineedit").draw(conW, unicode.safeTextFormat(leText, leCX)), 0xFFFFFF, 0)
end
end
local function drawDisplay()
for i = 1, #console do
if consoleShown[i] ~= console[i] or i == conCY or i == conCYShown then
draw(i)
consoleShown[i] = console[i]
end
end
conCYShown = conCY
end
-- Terminal Visual --
local function consoleSD()
for i = 1, #console - 1 do
console[i] = console[i + 1]
end
console[#console] = (" "):rep(conW)
end
local function consoleSU()
local backup = (" "):rep(conW)
for i = 1, #console do
backup, console[i] = console[i], backup
end
end
local function consoleCLS()
for i = 1, #console do
console[i] = (" "):rep(conW)
end
conCX, conCY = 1, 1
end
local function writeFF()
if conCY ~= #console then
conCY = conCY + 1
else
consoleSD()
end
end
local function writeData(data)
-- handle data until completion
while #data > 0 do
local char = unicode.sub(data, 1, 1)
--neo.emergency("svc-t.data: " .. char:byte())
data = unicode.sub(data, 2)
-- handle character
if char == "\t" then
-- not ideal, but allowed
char = " "
end
if char == "\r" then
conCX = 1
elseif char == "\x00" then
-- caused by TELNET \r rules
elseif char == "\n" then
conCX = 1
writeFF()
elseif char == "\a" then
-- Bell (er...)
elseif char == "\b" then
conCX = math.max(1, conCX - 1)
elseif char == "\v" or char == "\f" then
writeFF()
else
local charL = unicode.wlen(char)
if (conCX + charL - 1) > conW then
conCX = 1
writeFF()
end
local spaces = (" "):rep(charL - 1)
console[conCY] = unicode.sub(console[conCY], 1, conCX - 1) .. char .. spaces .. unicode.sub(console[conCY], conCX + charL)
conCX = conCX + charL
-- Cursor can be (intentionally!) off-screen here
end
end
end
local function writeANSI(s)
--neo.emergency("svc-t.ansi: " .. s)
-- This supports just about enough to get by.
if s == "c" then
consoleCLS()
return
end
local pfx = s:sub(1, 1)
local cmd = s:sub(#s)
if pfx == "[" then
local np = tonumber(s:sub(2, -2)) or 1
if cmd == "A" then
conCY = conCY - np
elseif cmd == "B" then
conCY = conCY + np
elseif cmd == "C" then
conCX = conCX + np
elseif cmd == "D" then
conCX = conCX - np
elseif cmd == "f" or cmd == "H" then
local p = s:find(";")
if not p then
conCY = np
conCX = 1
else
conCY = tonumber(s:sub(2, p - 1)) or 1
conCX = tonumber(s:sub(p + 1, -2)) or 1
end
elseif cmd == "J" then
consoleCLS()
elseif cmd == "K" then
if s == "[K" or s == "[0K" then
-- bash needs this
console[conCY] = unicode.sub(console[conCY], 1, conCX - 1) .. (" "):rep(1 + conW - conCX)
else
console[conCY] = (" "):rep(conW)
end
elseif cmd == "n" then
if s == "[6n" then
for _, v in pairs(sendSigs) do
v("data", "\x1b[" .. conY .. ";" .. conX .. "R")
end
end
elseif cmd == "s" then
conSCX, conSCY = conCX, conCY
elseif cmd == "u" then
conCX, conCY = conSCX, conSCY
end
end
conCX = math.min(math.max(math.floor(conCX), 1), conW)
conCY = math.min(math.max(math.floor(conCY), 1), #console)
end
-- The Terminus --
local tvBuildingCmd = ""
local tvBuildingUTF = ""
local tvSubnegotiation = false
local function incoming(s)
tvBuildingCmd = tvBuildingCmd .. s
-- Flush Cmd
while #tvBuildingCmd > 0 do
if tvBuildingCmd:byte() == 255 then
-- It's a command. Uhoh.
if #tvBuildingCmd < 2 then break end
local cmd = tvBuildingCmd:byte(2)
local param = tvBuildingCmd:byte(3)
local cmdLen = 2
-- Command Lengths
if cmd >= 251 and cmd <= 254 then cmdLen = 3 end
if #tvBuildingCmd < cmdLen then break end
if cmd == 240 then
-- SE
tvSubnegotiation = false
elseif cmd == 250 then
-- SB
tvSubnegotiation = true
elseif cmd == 251 and param == 1 then
-- WILL ECHO (respond with DO ECHO, disable line editing)
-- test using io.write("\xFF\xFB\x01")
for _, v in pairs(sendSigs) do
v("telnet", "\xFF\xFD\x01")
end
setLineEditing(false)
elseif cmd == 252 and param == 1 then
-- WON'T ECHO (respond with DON'T ECHO, enable line editing)
for _, v in pairs(sendSigs) do
v("telnet", "\xFF\xFE\x01")
end
setLineEditing(true)
elseif cmd == 251 or cmd == 252 then
-- WILL/WON'T (x) (respond with DON'T (X))
local res = "\xFF\xFE" .. string.char(param)
for _, v in pairs(sendSigs) do
v("telnet", res)
end
elseif cmd == 253 or cmd == 254 then
-- DO/DON'T (x) (respond with WON'T (X))
local res = "\xFF\xFC" .. string.char(param)
for _, v in pairs(sendSigs) do
v("telnet", res)
end
elseif cmd == 255 then
if not tvSubnegotiation then
tvBuildingUTF = tvBuildingUTF .. "\xFF"
end
end
tvBuildingCmd = tvBuildingCmd:sub(cmdLen + 1)
else
if not tvSubnegotiation then
tvBuildingUTF = tvBuildingUTF .. tvBuildingCmd:sub(1, 1)
end
tvBuildingCmd = tvBuildingCmd:sub(2)
end
end
-- Flush UTF/Display
while #tvBuildingUTF > 0 do
local head = tvBuildingUTF:byte()
local len = 1
local handled = false
if head == 27 then
local h2 = tvBuildingUTF:byte(2)
if h2 == 91 then
for i = 3, #tvBuildingUTF do
local cmd = tvBuildingUTF:byte(i)
if cmd >= 0x40 and cmd <= 0x7E then
writeANSI(tvBuildingUTF:sub(2, i))
len = i
handled = true
break
end
end
elseif h2 then
len = 2
writeANSI(tvBuildingUTF:sub(2, 2))
handled = true
end
if not handled then break end
end
if not handled then
if head < 192 then
len = 1
elseif head < 224 then
len = 2
elseif head < 240 then
len = 3
elseif head < 248 then
len = 4
elseif head < 252 then
len = 5
elseif head < 254 then
len = 6
end
if #tvBuildingUTF < len then
break
end
-- verified one full character...
writeData(tvBuildingUTF:sub(1, len))
end
tvBuildingUTF = tvBuildingUTF:sub(len + 1)
end
end
do
tReg(function (_, pid, sendSig)
sendSigs[pid] = sendSig
return {
id = "x." .. id,
pid = neo.pid,
write = function (text)
incoming(tostring(text))
drawDisplay()
end
}
end, true)
if retTbl then
coroutine.resume(coroutine.create(retTbl), {
access = "x." .. id,
close = function ()
closeNow = true
neo.scheduleTimer(0)
end
})
end
end
local control = false
local function key(a, c)
if control then
if c == 203 and conW > 8 then
setSize(conW - 1, #console)
return
elseif c == 200 and #console > 1 then
setSize(conW, #console - 1)
return
elseif c == 205 then
setSize(conW + 1, #console)
return
elseif c == 208 then
setSize(conW, #console + 1)
return
end
end
-- so with the reserved ones dealt with...
if not leText then
-- Line Editing not active.
-- For now support a bare minimum.
for _, v in pairs(sendSigs) do
if a == "\x03" then
v("telnet", "\xFF\xF4")
elseif c == 199 then
v("data", "\x1b[H")
elseif c == 201 then
v("data", "\x1b[5~")
elseif c == 207 then
v("data", "\x1b[F")
elseif c == 209 then
v("data", "\x1b[6~")
elseif c == 203 then
v("data", "\x1b[D")
elseif c == 205 then
v("data", "\x1b[C")
elseif c == 200 then
v("data", "\x1b[A")
elseif c == 208 then
v("data", "\x1b[B")
elseif a == "\r" then
v("data", "\r\n")
elseif a then
v("data", a)
end
end
elseif not control then
-- Line Editing active and control isn't involved
if c == 200 or c == 208 then
-- History cursor up (history down)
leText = leHistory[#leHistory]
leCX = unicode.len(leText) + 1
if c == 208 then
cycleHistoryUp()
else
cycleHistoryDown()
end
return
end
local lT, lC, lX = require("lineedit").key(a, c, leText, leCX)
leText = lT or leText
leCX = lC or leCX
if lX == "nl" then
cycleHistoryUp()
leHistory[#leHistory] = leText
-- the whole thing {
local fullText = leText .. "\r\n"
writeData(fullText)
drawDisplay()
for _, v in pairs(sendSigs) do
v("data", fullText)
end
-- }
leText = ""
leCX = 1
end
end
end
while not closeNow do
local e = {coroutine.yield()}
if e[1] == "k.procdie" then
sendSigs[e[3]] = nil
elseif e[1] == "x.neo.pub.window" then
if e[3] == "close" then
break
elseif e[3] == "clipboard" then
for i = 1, unicode.len(e[4]) do
local c = unicode.sub(e[4], i, i)
if c ~= "\r" then
if c == "\n" then
c = "\r"
end
key(c, 0)
end
end
draw(#console + 1)
elseif e[3] == "key" then
if e[5] == 29 or e[5] == 157 then
control = e[6]
elseif e[6] then
key(e[4] ~= 0 and unicode.char(e[4]), e[5])
draw(#console + 1)
end
elseif e[3] == "line" then
draw(e[4])
end
end
end

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- s-everest
@ -50,7 +46,7 @@ local monitors = {}
-- This is where we stuff processes until monitors show up
monitors[0] = {nil, nil, 160, 50}
-- {monitor, x, y, w, h, callback, title}
-- {monitor, x, y, w, h, callback}
-- callback events are:
-- key ka kc down
-- line y
@ -65,12 +61,10 @@ local shuttingDown = false
-- Also used for settings.
local savingThrow = neo.requestAccess("x.neo.sys.manage")
local draggingWindowX, draggingWindowY
local function suggestAppsStop()
for k, v in ipairs(surfaces) do
for i = 1, 4 do
v[6](v[8], "close")
v[6]("close")
end
end
end
@ -87,8 +81,8 @@ local function dying()
-- If a process evades the deathtrap then it clearly has reason to stay alive regardless of Everest status.
-- Also note, should savingThrow fail, neo.dead is now a thing.
for _, v in ipairs(surfaces) do
pcall(v[6], v[8], "line", 1)
pcall(v[6], v[8], "line", 2)
pcall(v[6], "line", 1)
pcall(v[6], "line", 2)
end
surfaces = {}
end
@ -146,13 +140,162 @@ local function doBackgroundLine(m, mg, bdx, bdy, bdl)
local str = unicode.sub(statusLine, bdx, bdx + bdl - 1)
local strl = unicode.len(str)
pcall(mg.set, bdx, bdy, unicode.undoSafeTextFormat(str))
pcall(mg.set, bdx + strl, bdy, (" "):rep(bdl - strl))
pcall(mg.fill, bdx + strl, bdy, bdl - strl, 1, " ")
else
monitorGPUColours(m, mg, 0x000040, 0)
pcall(mg.set, bdx, bdy, (" "):rep(bdl))
pcall(mg.fill, bdx, bdy, bdl, 1, " ")
end
end
local function updateRegion(monitorId, x, y, w, h, surfaceSpanCache)
if not renderingAllowed() then return end
local m = monitors[monitorId]
local mg, rb = m[1]()
if not mg then return end
if rb then
monitorResetBF(m)
end
-- The input region is the one that makes SENSE.
-- Considering WC handling, that's not an option.
-- WCHAX: start
if x > 1 then
x = x - 1
w = w + 1
end
-- this, in combination with 'forcefully blank out last column of window during render',
-- cleans up littering
w = w + 1
-- WCHAX: end
for span = 1, h do
local backgroundMarkStart = nil
for sx = 1, w do
local t, tx, ty = surfaceAt(monitorId, sx + x - 1, span + y - 1)
if t then
-- Background must occur first due to wide char weirdness
if backgroundMarkStart then
local bdx, bdy, bdl = backgroundMarkStart + x - 1, span + y - 1, sx - backgroundMarkStart
doBackgroundLine(m, mg, bdx, bdy, bdl)
backgroundMarkStart = nil
end
if not surfaceSpanCache[monitorId .. "_" .. t .. "_" .. ty] then
surfaceSpanCache[monitorId .. "_" .. t .. "_" .. ty] = true
surfaces[t][6]("line", ty)
end
elseif not backgroundMarkStart then
backgroundMarkStart = sx
end
end
if backgroundMarkStart then
doBackgroundLine(monitors[monitorId], mg, backgroundMarkStart + x - 1, span + y - 1, (w - backgroundMarkStart) + 1)
end
end
end
local function updateStatus()
statusLine = "Λ-¶: menu (launch 'control' to logout)"
if surfaces[1] then
if #monitors > 1 then
-- 123456789X123456789X123456789X123456789X123456789X
statusLine = "Λ-+: move, Λ-Z: switch, Λ-X: swMonitor, Λ-C: close"
else
statusLine = "Λ-+: move, Λ-Z: switch, Λ-C: close"
end
end
statusLine = unicode.safeTextFormat(statusLine)
for k, v in ipairs(monitors) do
updateRegion(k, 1, 1, v[3], 1, {})
end
end
local function ensureOnscreen(monitor, x, y, w, h)
if monitor <= 0 then monitor = #monitors end
if monitor >= (#monitors + 1) then monitor = 1 end
-- Failing anything else, revert to monitor 0
if #monitors == 0 then monitor = 0 end
x = math.min(math.max(1, x), monitors[monitor][3] - (w - 1))
y = math.max(1, math.min(monitors[monitor][4] - (h - 1), y))
return monitor, x, y
end
-- This is the "a state change occurred" function, only for use when needed
local function reconcileAll()
for k, v in ipairs(surfaces) do
-- About to update whole screen anyway so avoid the wait.
v[1], v[2], v[3] = ensureOnscreen(v[1], v[2], v[3], v[4], v[5])
end
local k = 1
while k <= #monitors do
local v = monitors[k]
local mon, rb = v[1]()
if rb then
monitorResetBF(v)
end
if mon then
-- This *can* return null if something went wonky. Let's detect that
v[3], v[4] = mon.getResolution()
if not v[3] then
neo.emergency("everest: monitor went AWOL and nobody told me u.u")
table.remove(monitors, k)
v = nil
end
end
if v then
updateRegion(k, 1, 1, v[3], v[4], {})
k = k + 1
end
end
updateStatus()
end
-- NOTE: If the M, X, Y, W and H are the same, this function ignores you, unless you put , true on the end.
local function moveSurface(surface, m, x, y, w, h, force)
local om, ox, oy, ow, oh = table.unpack(surface, 1, 5)
m = m or om
x = x or ox
y = y or oy
w = w or ow
h = h or oh
surface[1], surface[2], surface[3], surface[4], surface[5] = m, x, y, w, h
local cache = {}
if om == m and ow == w and oh == h then
if ox == x and oy == y and not force then
return
end
-- note: this doesn't always work due to WC support, and due to resize-to-repaint
if renderingAllowed() and not force then
local cb, b = monitors[m][1]()
if b then
monitorResetBF(monitors[m])
end
if cb then
pcall(cb.copy, ox, oy, w, h, x - ox, y - oy)
if surface == surfaces[1] then
local cacheControl = {}
for i = 1, h do
cacheControl[om .. "_1_" .. i] = true
end
updateRegion(om, ox, oy, ow, oh, cacheControl)
return
end
end
end
end
updateRegion(om, ox, oy, ow, oh, cache)
updateRegion(m, x, y, w, h, cache)
end
-- Returns offset from where we expected to be to where we are.
local function ofsSurface(focus, dx, dy)
local exX, exY = focus[2] + dx, focus[3] + dy
local m, x, y = ensureOnscreen(focus[1], exX, exY, focus[4], focus[5])
moveSurface(focus, nil, x, y)
return focus[2] - exX, focus[3] - exY
end
local function ofsMSurface(focus, dm)
local m, x, y = ensureOnscreen(focus[1] + dm, focus[2], focus[3], focus[4], focus[5])
moveSurface(focus, m, x, y)
end
local function handleSpan(target, x, y, text, bg, fg)
if not renderingAllowed() then return end
local m = monitors[target[1]]
@ -210,178 +353,6 @@ local function handleSpan(target, x, y, text, bg, fg)
submitSegment()
end
local function updateRegion(monitorId, x, y, w, h, surfaceSpanCache)
if not renderingAllowed() then return end
local m = monitors[monitorId]
local mg, rb = m[1]()
if not mg then return end
if rb then
monitorResetBF(m)
end
-- The input region is the one that makes SENSE.
-- Considering WC handling, that's not an option.
-- WCHAX: start
if x > 1 then
x = x - 1
w = w + 1
end
-- this, in combination with 'forcefully blank out last column of window during render',
-- cleans up littering
w = w + 1
-- WCHAX: end
for span = 1, h do
local backgroundMarkStart = nil
for sx = 1, w do
local t, tx, ty = surfaceAt(monitorId, sx + x - 1, span + y - 1)
if t then
-- Background must occur first due to wide char weirdness
if backgroundMarkStart then
local bdx, bdy, bdl = backgroundMarkStart + x - 1, span + y - 1, sx - backgroundMarkStart
doBackgroundLine(m, mg, bdx, bdy, bdl)
backgroundMarkStart = nil
end
if not surfaceSpanCache[monitorId .. "_" .. t .. "_" .. ty] then
surfaceSpanCache[monitorId .. "_" .. t .. "_" .. ty] = true
if ty == 1 then
local lw = surfaces[t][4]
local bg = 0x0080FF
local fg = 0x000000
local tx = "-"
if t == 1 then
bg = 0x000000
fg = 0x0080FF
tx = "+"
end
local vtitle = surfaces[t][7]
local vto = unicode.len(vtitle)
if vto < lw then
vtitle = vtitle .. (tx):rep(lw - vto)
else
vtitle = unicode.sub(vtitle, 1, lw)
end
handleSpan(surfaces[t], 1, 1, vtitle, bg, fg)
else
surfaces[t][6](surfaces[t][8], "line", ty - 1)
end
end
elseif not backgroundMarkStart then
backgroundMarkStart = sx
end
end
if backgroundMarkStart then
doBackgroundLine(monitors[monitorId], mg, backgroundMarkStart + x - 1, span + y - 1, (w - backgroundMarkStart) + 1)
end
end
end
local function updateStatus()
statusLine = "Λ-¶: menu (launch 'control' to logout)"
if surfaces[1] then
if #monitors > 1 then
-- 123456789X123456789X123456789X123456789X123456789X
statusLine = "Λ-+: move, Λ-Z: switch, Λ-X: swMonitor, Λ-C: close"
else
statusLine = "Λ-+: move, Λ-Z: switch, Λ-C: close"
end
end
statusLine = unicode.safeTextFormat(statusLine)
for k, v in ipairs(monitors) do
updateRegion(k, 1, 1, v[3], 1, {})
end
end
local function ensureOnscreen(monitor, x, y, w, h)
if monitor <= 0 then monitor = #monitors end
if monitor >= (#monitors + 1) then monitor = 1 end
-- Failing anything else, revert to monitor 0
if #monitors == 0 then monitor = 0 end
x = math.min(math.max(x, 1), monitors[monitor][3] - (w - 1))
y = math.max(2 - h, math.min(monitors[monitor][4], y))
return monitor, x, y
end
-- This is the "a state change occurred" function, only for use when needed
local function reconcileAll()
for k, v in ipairs(surfaces) do
-- About to update whole screen anyway so avoid the wait.
v[1], v[2], v[3] = ensureOnscreen(v[1], v[2], v[3], v[4], v[5])
end
local k = 1
while k <= #monitors do
local v = monitors[k]
local mon, rb = v[1]()
if rb then
monitorResetBF(v)
end
if mon then
-- This *can* return null if something went wonky. Let's detect that
v[3], v[4] = mon.getResolution()
if not v[3] then
neo.emergency("everest: monitor went AWOL and nobody told me u.u")
table.remove(monitors, k)
v = nil
end
end
if v then
updateRegion(k, 1, 1, v[3], v[4], {})
k = k + 1
end
end
updateStatus()
end
-- NOTE: If the M, X, Y, W and H are the same, this function ignores you, unless you put , true on the end.
local function moveSurface(surface, m, x, y, w, h, force)
local om, ox, oy, ow, oh = table.unpack(surface, 1, 5)
m = m or om
x = x or ox
y = y or oy
w = w or ow
h = h or oh
surface[1], surface[2], surface[3], surface[4], surface[5] = m, x, y, w, h
local cache = {}
if om == m and ow == w and oh == h then
if ox == x and oy == y and not force then
return
end
-- note: this doesn't always work due to WC support, and due to resize-to-repaint
if renderingAllowed() and not force then
local cb, b = monitors[m][1]()
if b then
monitorResetBF(monitors[m])
end
if cb then
local monH = monitors[m][4]
local cutTop = math.max(0, 1 - math.min(oy, y))
local cutBottom = math.max(0, (math.max(oy + oh, y + h) - 1) - monH)
pcall(cb.copy, ox, oy + cutTop, w, h - (cutTop + cutBottom), x - ox, y - oy)
if surface == surfaces[1] then
local cacheControl = {}
for i = 1 + cutTop, h - cutBottom do
cacheControl[om .. "_1_" .. i] = true
end
updateRegion(om, ox, oy, ow, oh, cacheControl)
return
end
end
end
end
updateRegion(om, ox, oy, ow, oh, cache)
updateRegion(m, x, y, w, h, cache)
end
-- Returns offset from where we expected to be to where we are.
local function ofsSurface(focus, dx, dy)
local exX, exY = focus[2] + dx, focus[3] + dy
local m, x, y = ensureOnscreen(focus[1], exX, exY, focus[4], focus[5])
moveSurface(focus, nil, x, y)
return focus[2] - exX, focus[3] - exY
end
local function ofsMSurface(focus, dm)
local m, x, y = ensureOnscreen(focus[1] + dm, focus[2], focus[3], focus[4], focus[5])
moveSurface(focus, m, x, y)
end
local basePalT2 = {
-- on T2 we provide 'system colours' by default
0x000000, 0x0080FF, 0x000040, 0xFFFFFF,
@ -445,11 +416,11 @@ local function changeFocus(oldSurface, optcache)
if ns1 ~= oldSurface then
if oldSurface then
setSurfacePalette(oldSurface, nil)
oldSurface[6](oldSurface[8], "focus", false)
oldSurface[6]("focus", false)
end
if ns1 then
setSurfacePalette(ns1, nil)
ns1[6](ns1[8], "focus", true)
ns1[6]("focus", true)
end
updateStatus()
if oldSurface then
@ -488,12 +459,71 @@ everestProvider(function (pkg, pid, sendSig)
else
title = base .. ":" .. title
end
local llid = lid
lid = lid + 1
local surf = {math.min(#monitors, math.max(1, lIM)), 1, 2, w, h, sendSig, title, llid}
local surf = {math.min(#monitors, math.max(1, lIM)), 1, 2, w, h}
if h >= monitors[surf[1]][4] then
surf[3] = 1
end
local focusState = false
local llid = lid
lid = lid + 1
local specialDragHandler
surf[6] = function (ev, a, b, c, d, e)
-- Must forward surface events
if ev == "focus" then
focusState = a
end
if ev == "touch" then
specialDragHandler = nil
if math.floor(b) == 1 then
if e == 1 then
sendSig(llid, "close")
return
end
specialDragHandler = function (x, y)
local ofsX, ofsY = math.floor(x) - math.floor(a), math.floor(y) - math.floor(b)
if (ofsX == 0) and (ofsY == 0) then return end
ofsSurface(surf, ofsX, ofsY)
end
return
end
b = b - 1
end
if ev == "drag" then
if specialDragHandler then
specialDragHandler(a, b)
return
end
b = b - 1
end
if ev == "scroll" or ev == "drop" then
specialDragHandler = nil
b = b - 1
end
if ev == "line" then
if a == 1 then
local lw = surf[4]
local bg = 0x0080FF
local fg = 0x000000
local tx = "-"
if focusState then
bg = 0x000000
fg = 0x0080FF
tx = "+"
end
local vtitle = title
local vto = unicode.len(vtitle)
if vto < lw then
vtitle = vtitle .. (tx):rep(lw - vto)
else
vtitle = unicode.sub(vtitle, 1, lw)
end
handleSpan(surf, 1, 1, vtitle, bg, fg)
return
end
a = a - 1
end
sendSig(llid, ev, a, b, c, d, e)
end
local osrf = surfaces[1]
table.insert(surfaces, 1, surf)
surfaceOwners[surf] = pid
@ -521,13 +551,17 @@ everestProvider(function (pkg, pid, sendSig)
end,
span = function (x, y, text, bg, fg)
if neo.dead then error("everest died") end
checkArg(3, text, "string")
if type(x) ~= "number" then error("X must be number.") end
if type(y) ~= "number" then error("Y must be number.") end
if type(bg) ~= "number" then error("Background must be number.") end
if type(fg) ~= "number" then error("Foreground must be number.") end
if type(text) ~= "string" then error("Text must be string.") end
x, y, bg, fg = math.floor(x), math.floor(y), math.floor(bg), math.floor(fg)
if y == 0 then return end
handleSpan(surf, x, y + 1, text, bg, fg)
end,
recommendPalette = function (pal)
if surfaces[1] ~= surf then return 0 end
if not focusState then return 0 end
return setSurfacePalette(surf, pal)
end,
close = function ()
@ -552,7 +586,7 @@ end)
-- THE EVEREST USER API ENDS (now for the session API, which just does boring stuff)
-- used later on for lost monitor, too
local function disclaimMonitor(mon)
checkArg(1, mon, "string")
neo.ensureType(mon, "string")
screens.disclaim(mon)
for k, v in ipairs(monitors) do
if v[2] == mon then
@ -566,7 +600,7 @@ end
everestSessionProvider(function (pkg, pid, sendSig)
return {
endSession = function (gotoBristol)
checkArg(1, gotoBristol, "boolean")
neo.ensureType(gotoBristol, "boolean")
shuttingDown = true
if gotoBristol then
suggestAppsStop()
@ -604,59 +638,69 @@ end
local isAltDown = false
local isCtrDown = false
local function key(ku, ka, kc, down)
local ku, lin = screens.getMonitorByKeyboard(ku)
local ku = screens.getMonitorByKeyboard(ku)
if not ku then return end
for k, v in ipairs(monitors) do
if ku and v[2] == ku then
lin = k
break
if v[2] == mu then
lIM = k
end
end
if not lin then return end
lIM = lin
local focus = surfaces[1]
if kc == 29 then
isCtrDown = down
elseif kc == 56 then
isAltDown = down
end
if isAltDown and ka == 122 then
if focus and down then
local n = table.remove(surfaces, 1)
table.insert(surfaces, n)
changeFocus(n)
if kc == 29 then isCtrDown = down end
if kc == 56 then isAltDown = down end
if isAltDown then
if ka == 120 then
if focus and down then ofsMSurface(focus, 1) end return
end
elseif isAltDown and kc == 200 then
if focus and down then ofsSurface(focus, 0, -1) end
elseif isAltDown and kc == 208 then
if focus and down then ofsSurface(focus, 0, 1) end
elseif isAltDown and kc == 203 then
if focus and down then ofsSurface(focus, -1, 0) end
elseif isAltDown and kc == 205 then
if focus and down then ofsSurface(focus, 1, 0) end
elseif isAltDown and ka == 120 then
if focus and down then ofsMSurface(focus, 1) end
elseif isAltDown and ka == 97 then
if not down then
isAltDown = false
if kc == 200 then
if focus and down then ofsSurface(focus, 0, -1) end return
end
elseif isAltDown and (ka == 3 or ka == 99) then
if down then
if kc == 208 then
if focus and down then ofsSurface(focus, 0, 1) end return
end
if kc == 203 then
if focus and down then ofsSurface(focus, -1, 0) end return
end
if kc == 205 then
if focus and down then ofsSurface(focus, 1, 0) end return
end
if ka == 122 then
if focus and down then
local n = table.remove(surfaces, 1)
table.insert(surfaces, n)
changeFocus(n)
end return
end
if ka == 3 then
-- Ctrl-Alt-C (!?!?!!)
if isCtrDown then
error("User-authorized Everest crash.")
elseif focus then
focus[6](focus[8], "close")
end
end
elseif isAltDown and ka == 13 then
if down then
startLauncher()
if ka == 99 then
if down then
if isCtrDown then
error("User-authorized Everest crash.")
else
if focus then
focus[6]("close")
end
end
end
return
end
elseif focus then
if ka == 13 then
if down then
startLauncher()
end
return
end
end
if focus then
if kc ~= 56 then
lIM = focus[1]
end
focus[6](focus[8], "key", ka, kc, down)
focus[6]("key", ka, kc, down)
end
end
@ -689,7 +733,7 @@ while not shuttingDown do
end
if s[1] == "h.clipboard" then
if surfaces[1] then
surfaces[1][6](surfaces[1][8], "clipboard", s[3])
surfaces[1][6]("clipboard", s[3])
end
end
-- next on my list: high-res coordinates
@ -705,16 +749,7 @@ while not shuttingDown do
local ns = table.remove(surfaces, sid)
table.insert(surfaces, 1, ns)
changeFocus(os)
draggingWindowX, draggingWindowY = nil
if ly == 1 then
if s[5] == 1 then
ns[6](ns[8], "close")
else
draggingWindowX, draggingWindowY = lx, ly
end
else
ns[6](ns[8], "touch", lx, ly - 1, ix, iy, s[5])
end
ns[6]("touch", lx, ly, ix, iy, s[5])
else
if s[5] == 1 then startLauncher() end
end
@ -731,16 +766,8 @@ while not shuttingDown do
if k == focus[1] then
local x, y = (math.ceil(s[3]) - focus[2]) + 1, (math.ceil(s[4]) - focus[3]) + 1
local ix, iy = s[3] - math.floor(s[3]), s[4] - math.floor(s[4])
if s[1] == "h.drag" and draggingWindowX then
local ofsX, ofsY = x - draggingWindowX, y - draggingWindowY
if ofsX ~= 0 or ofsY ~= 0 then
ofsSurface(focus, ofsX, ofsY)
end
else
draggingWindowX, draggingWindowY = nil
-- Ok, so let's see...
focus[6](focus[8], s[1]:sub(3), x, y - 1, ix, iy, s[5])
end
-- Ok, so let's see...
focus[6](s[1]:sub(3), x, y, ix, iy, s[5])
end
break
end
@ -772,7 +799,7 @@ while not shuttingDown do
if os1 then
changeFocus(os1)
else
changeFocus()
changeFocus(surfaces[1])
end
end
if s[1] == "x.neo.sys.screens" then

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- s-donkonit : config, shutdown, screens
@ -129,7 +125,7 @@ local mBase = {
end
return s
end,
delSetting = function (name)
delSetting = function ()
neo.ensureType(name, "string")
local val = nil
if name == "password" or name == "pub.clipboard" then val = "" end

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- s-icecap : Responsible for x.neo.pub API, crash dialogs, and security policy that isn't "sys- has ALL access, anything else has none"
-- In general, this is what userspace will be interacting with in some way or another to get stuff done
@ -85,12 +81,20 @@ local function getPfx(xd, pkg)
end
end
local function splitAC(ac)
local sb = ac:match("/[a-z0-9/%.]*$")
if sb then
return ac:sub(1, #ac - #sb), sb
local endAcPattern = "/[a-z0-9/%.]*$"
local function matchesSvc(xd, pkg, perm)
local pfx = getPfx(xd, pkg)
if pfx then
local permAct = perm
local paP = permAct:match(endAcPattern)
if paP then
permAct = permAct:sub(1, #permAct - #paP)
end
if permAct == pfx then
return "allow"
end
end
return ac
end
donkonitDFProvider(function (pkg, pid, sendSig)
@ -105,20 +109,17 @@ donkonitDFProvider(function (pkg, pid, sendSig)
end
end
return {
showFileDialogAsync = function (forWrite, defName)
showFileDialogAsync = function (forWrite)
if not rawequal(forWrite, nil) then
require("sys-filewrap").ensureMode(forWrite)
end
if not rawequal(defName, nil) then
defName = tostring(defName)
end
-- Not hooked into the event API, so can't safely interfere
-- Thus, this is async and uses a return event.
local tag = {}
neo.scheduleTimer(0)
table.insert(todo, function ()
-- sys-filedialog is yet another "library to control memory usage".
local closer = require("sys-filedialog")(event, nexus, function (res) openHandles[tag] = nil sendSig("filedialog", tag, res) end, neo.requireAccess("c.filesystem", "file managers"), pkg, forWrite, defName)
local closer = require("sys-filedialog")(event, nexus, function (res) openHandles[tag] = nil sendSig("filedialog", tag, res) end, neo.requireAccess("c.filesystem", "file managers"), pkg, forWrite)
openHandles[tag] = closer
end)
return tag
@ -126,8 +127,7 @@ donkonitDFProvider(function (pkg, pid, sendSig)
myApi = getPfx("", pkg),
lockPerm = function (perm)
-- Are we allowed to?
local permPfx, detail = splitAC(perm)
if getPfx("x.", pkg) ~= permPfx then
if not matchesSvc("x.", pkg, perm) then
return false, "You don't own this permission."
end
local set = "perm|*|" .. perm
@ -220,11 +220,7 @@ local function secPolicyStage2(pid, proc, perm, req)
-- Push to ICECAP thread to avoid deadlock b/c wrong event-pull context
neo.scheduleTimer(0)
table.insert(todo, function ()
local fPerm = perm
if fPerm:sub(1, 2) == "r." then
fPerm = splitAC(fPerm)
end
local ok, err = pcall(secpol, nexus, settings, proc.pkg, pid, fPerm, req, getPfx("", proc.pkg))
local ok, err = pcall(secpol, nexus, settings, proc.pkg, pid, perm, req, matchesSvc)
if not ok then
neo.emergency("Used fallback policy because of run-err: " .. err)
req(def)
@ -244,7 +240,11 @@ rootAccess.securityPolicy = function (pid, proc, perm, req)
end
-- Do we need to start it?
if perm:sub(1, 6) == "x.svc." and not neo.usAccessExists(perm) then
local appAct = splitAC(perm:sub(7))
local appAct = perm:sub(7)
local paP = appAct:match(endAcPattern)
if paP then
appAct = appAct:sub(1, #appAct - #paP)
end
-- Prepare for success
onReg[perm] = onReg[perm] or {}
local orp = onReg[perm]

View File

@ -1,27 +1,21 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- s-bristol splash screen, login agent
-- Named to allude to Plymouth (splash screen used in some linux distros)
local callerPkg, callerPid, callerScr = ...
local gpuG, screen = nil, nil
local shutdownEmergency = neo.requestAccess("k.computer").shutdown
neo.requestAccess("s.h.key_down")
neo.requestAccess("s.h._kosneo_syslog")
-- gpuG/performDisclaim are GPU management, while screen is used for prioritization
local gpuG, performDisclaim, screen = nil
local scrW, scrH = 1, 1
local nssInst
local console = {}
local helpActive = false
local buttonsActive = false
local scrW, scrH
local warnings = {
"",
"",
""
}
-- Attempts to call upon nsm for a safe shutdown
local function shutdown(reboot)
@ -36,53 +30,32 @@ local function shutdown(reboot)
end
end
local function basicDraw(bg)
local gpu = gpuG()
pcall(gpu.setBackground, bg or 0xFFFFFF)
local function rstfbDraw(gpu)
pcall(gpu.setBackground, 0xFFFFFF)
pcall(gpu.setForeground, 0x000000)
end
local function basicDraw(gpu)
local ok, sw, sh = pcall(gpu.getResolution)
if not ok then return end
scrW, scrH = sw, sh
pcall(gpu.fill, 1, 1, scrW, scrH, " ")
pcall(gpu.set, 2, 2, "KittenOS NEO")
end
local function advDraw(gpu)
basicDraw(gpu)
local usage = math.floor((os.totalMemory() - os.freeMemory()) / 1024)
pcall(gpu.set, 2, 3, "RAM Usage: " .. usage .. "K / " .. math.floor(os.totalMemory() / 1024) .. "K")
local cut = 7
if buttonsActive then cut = 9 end
local areaSize = scrH - cut
local n2 = 0
if helpActive then
if _VERSION == "Lua 5.2" then
table.insert(console, "WARNING: Lua 5.2 memory usage issue!")
table.insert(console, "Shift-right-click while holding the CPU/APU.")
n2 = 2
end
table.insert(console, "TAB to change option, ENTER to select.")
n2 = n2 + 1
end
for i = 1, areaSize do
pcall(gpu.set, 2, 6 + i, console[#console + i - areaSize] or "")
end
for i = 1, n2 do
table.remove(console, #console)
end
return gpu
end
local function consoleEventHandler(ev)
if ev[1] == "h._kosneo_syslog" then
local text = ""
for i = 3, #ev do
if i ~= 3 then text = text .. " " end
text = text .. tostring(ev[i])
end
table.insert(console, text)
for i = 1, #warnings do
pcall(gpu.set, 2, 6 + i, warnings[i])
end
end
-- Attempts to get an NSS monitor with a priority list of screens
local function retrieveNssMonitor(...)
local spc = {...}
-- Callback setup by finalPrompt to disclaim the main monitor first
local performDisclaim = nil
local function retrieveNssMonitor(nss)
gpuG = nil
local subpool = {}
while not gpuG do
@ -94,23 +67,19 @@ local function retrieveNssMonitor(...)
-- If no monitors are available, shut down now.
-- NSS monitor pool output is smaller than, but similar to, Everest monitor data:
-- {gpu, screenAddr}
local pool = nssInst.getClaimable()
local pool = nss.getClaimable()
while not pool[1] do
-- wait for presumably a NSS notification
consoleEventHandler({coroutine.yield()})
pool = nssInst.getClaimable()
coroutine.yield() -- wait for presumably a NSS notification
pool = nss.getClaimable()
end
subpool = {}
-- Specifies which element to elevate to top priority
local optimalSwap = nil
local optimal = #spc + 1
if screen then
for k, v in ipairs(pool) do
for k2, v2 in ipairs(spc) do
if v == v2 and optimal > k2 then
optimalSwap, optimal = k, k2
break
end
if v == screen then
optimalSwap = k
break
end
end
end
@ -120,7 +89,7 @@ local function retrieveNssMonitor(...)
pool[1] = swapA
end
for _, v in ipairs(pool) do
local gpu = nssInst.claim(v)
local gpu = nss.claim(v)
if gpu then
local gcb = gpu()
if gcb then
@ -133,53 +102,52 @@ local function retrieveNssMonitor(...)
end
end
end
if subpool[1] then
gpuG, screen = table.unpack(subpool[1])
if not subpool[1] then
error("None of the GPUs we got were actually usable")
end
gpuG = subpool[1][1]
screen = subpool[1][2]
end
-- done with search
local gpu = gpuG()
rstfbDraw(gpu)
performDisclaim = function (full)
nssInst.disclaim(subpool[1][2])
nss.disclaim(subpool[1][2])
if full then
for _, v in ipairs(subpool) do
nssInst.disclaim(v[2])
nss.disclaim(v[2])
end
end
end
return gpu
end
local function sleep(t)
neo.scheduleTimer(os.uptime() + t)
while true do
local ev = {coroutine.yield()}
consoleEventHandler(ev)
if ev[1] == "k.timer" then
break
end
if ev[1] == "x.neo.sys.screens" then
retrieveNssMonitor(screen)
basicDraw()
-- This implies we have and can use nss, but check anyway
local nss = neo.requestAccess("x.neo.sys.screens")
if nss then
local gpu = retrieveNssMonitor(nss)
basicDraw(gpu)
end
end
end
end
local function alert(s)
console = {s}
helpActive, buttonsActive = false, false
basicDraw()
sleep(1)
buttonsActive = true
end
local function finalPrompt()
nssInst = neo.requestAccess("x.neo.sys.screens")
if not nssInst then
console = {"sys-glacier not available"}
basicDraw()
error("no nssInst")
local nss = neo.requestAccess("x.neo.sys.screens")
if nss then
retrieveNssMonitor(nss)
else
error("no glacier to provide GPU for the prompt")
end
retrieveNssMonitor()
helpActive, buttonsActive = true, true
-- This is nsm's final chance to make itself available and thus allow the password to be set
local nsm = neo.requestAccess("x.neo.sys.manage")
local waiting = true
@ -191,11 +159,24 @@ local function finalPrompt()
return false
end
end
if _VERSION == "Lua 5.2" then
warnings[1] = "NOTE: It's recommended you use Lua 5.3."
warnings[2] = "Shift-right-click while holding the CPU item."
else
warnings[1] = "TAB to change option,"
warnings[2] = "ENTER to select..."
end
-- The actual main prompt loop
while waiting do
local gpu = gpuG()
rstfbDraw(gpu)
advDraw(gpu)
local entry = ""
local entry2 = ""
local active = true
local shButton = "<Shutdown>"
local rbButton = "<Reboot>"
local smButton = "<Safe Mode>"
local pw = {function ()
return "Password: " .. entry2
end, function (key)
@ -207,7 +188,10 @@ local function finalPrompt()
if entry == password then
waiting = false
else
alert("Incorrect password")
local gpu = gpuG()
rstfbDraw(gpu)
advDraw(gpu)
sleep(1)
end
active = false
end
@ -224,69 +208,68 @@ local function finalPrompt()
end
local controls = {
{function ()
return "<Shutdown>"
return shButton
end, function (key)
if key == 13 then
alert("Shutting down...")
local gpu = gpuG()
rstfbDraw(gpu)
basicDraw(gpu)
pcall(gpu.set, 2, 4, "Shutting down...")
shutdown(false)
end
end, 2, scrH - 1, 10},
end, 2, scrH - 1, unicode.len(shButton)},
{function ()
return "<Reboot>"
return rbButton
end, function (key)
if key == 13 then
alert("Rebooting...")
local gpu = gpuG()
rstfbDraw(gpu)
basicDraw(gpu)
pcall(gpu.set, 2, 4, "Rebooting...")
shutdown(true)
end
end, 13, scrH - 1, 8},
end, 3 + unicode.len(shButton), scrH - 1, unicode.len(rbButton)},
{function ()
return "<Safe Mode>"
return smButton
end, function (key)
if key == 13 then
local gpu = gpuG()
rstfbDraw(gpu)
basicDraw(gpu)
pcall(gpu.set, 2, 4, "Login to activate Safe Mode.")
sleep(1)
gpu = gpuG()
safeModeActive = true
alert("Login to activate Safe Mode.")
rstfbDraw(gpu)
advDraw(gpu)
end
end, 22, scrH - 1, 11},
end, 4 + unicode.len(shButton) + unicode.len(rbButton), scrH - 1, unicode.len(smButton)},
pw,
}
local control = #controls
local lastKeyboard
while active do
local gpu = basicDraw()
if gpu then
for k, v in ipairs(controls) do
if k == control then
pcall(gpu.setBackground, 0x000000)
pcall(gpu.setForeground, 0xFFFFFF)
else
pcall(gpu.setBackground, 0xFFFFFF)
pcall(gpu.setForeground, 0x000000)
end
pcall(gpu.fill, v[3], v[4], v[5], 1, " ")
pcall(gpu.set, v[3], v[4], v[1]())
local gpu = gpuG()
for k, v in ipairs(controls) do
if k == control then
pcall(gpu.setBackground, 0x000000)
pcall(gpu.setForeground, 0xFFFFFF)
else
pcall(gpu.setBackground, 0xFFFFFF)
pcall(gpu.setForeground, 0x000000)
end
pcall(gpu.fill, v[3], v[4], v[5], 1, " ")
pcall(gpu.set, v[3], v[4], v[1]())
end
-- event handling...
local sig = {coroutine.yield()}
consoleEventHandler(sig)
if sig[1] == "x.neo.sys.screens" then
-- We need to reinit screens no matter what.
retrieveNssMonitor(screen)
active = false
retrieveNssMonitor(nss)
end
if sig[1] == "h.key_down" then
if sig[2] ~= lastKeyboard then
lastKeyboard = sig[2]
local nScreen = nssInst.getMonitorByKeyboard(lastKeyboard)
if nScreen and nScreen ~= screen then
neo.emergency("new primary:", nScreen)
retrieveNssMonitor(nScreen, screen)
active = false
end
end
if sig[4] == 15 then
-- this makes sense in context
control = control % #controls
control = control % (#controls)
control = control + 1
else
controls[control][2](sig[3])
@ -294,21 +277,24 @@ local function finalPrompt()
end
end
end
helpActive, buttonsActive = false, false
local gpu = gpuG()
rstfbDraw(gpu)
advDraw(gpu)
return safeModeActive
end
local function postPrompt()
local gpu = gpuG()
local nsm = neo.requestAccess("x.neo.sys.manage")
local sh = "sys-everest"
console = {"Unable to get shell (no sys-glacier)"}
warnings = {"Unable to get sys-init.shell due to no NSM, using sys-everest"}
if nsm then
sh = nsm.getSetting("sys-init.shell") or sh
console = {"Starting " .. sh}
warnings = {"Starting " .. sh}
end
basicDraw()
rstfbDraw(gpu)
advDraw(gpu)
performDisclaim()
neo.executeAsync(sh)
-- There's a delay here to allow taking the monitor.
sleep(0.5)
for i = 1, 9 do
local v = neo.requestAccess("x.neo.sys.session")
@ -318,16 +304,16 @@ local function postPrompt()
end
end
-- ...oh. hope this works then?
console = {"x.neo.sys.session not found, try Safe Mode."}
retrieveNssMonitor(screen)
basicDraw()
warnings = {"That wasn't a shell. Try Safe Mode."}
rstfbDraw(gpu)
advDraw(gpu)
sleep(1)
shutdown(true)
end
local function initializeSystem()
-- System has just booted, bristol is in charge
-- No screen configuration, so just guess.
-- Firstly, since we don't know scrcfg, let's work out something sensible.
-- Note that we should try to keep going with this if there's no reason to do otherwise.
local gpuAc = neo.requestAccess("c.gpu")
local screenAc = neo.requestAccess("c.screen")
@ -354,36 +340,55 @@ local function initializeSystem()
gW, gH = math.min(80, gW), math.min(25, gH)
pcall(gpu.setResolution, gW, gH)
pcall(gpu.setDepth, gpu.maxDepth()) -- can crash on OCEmu if done at the "wrong time"
pcall(gpu.setForeground, 0x000000)
end
-- Setup the new GPU provider
gpuG = function () return gpu end
--
local w = 1
local steps = {"sys-glacier"}
for i = 1, 4 do table.insert(steps, "WAIT") end
table.insert(steps, "INJECT")
for i = 1, 8 do table.insert(steps, "WAIT") end
local steps = {
"sys-glacier", -- (Glacier : Config, Screen, Power)
-- Let that start, and system GC
"WAIT",
"WAIT",
"WAIT",
"WAIT",
-- Start services
"INJECT",
-- extra GC time
"WAIT",
"WAIT",
"WAIT",
"WAIT",
"WAIT",
"WAIT",
"WAIT"
}
local stepCount = #steps
neo.scheduleTimer(os.uptime())
while true do
local ev = {coroutine.yield()}
consoleEventHandler(ev)
if ev[1] == "k.procnew" then
table.insert(warnings, ev[2] .. "/" .. ev[3] .. " UP")
end
if ev[1] == "k.procdie" then
table.insert(warnings, ev[2] .. "/" .. ev[3] .. " DOWN")
table.insert(warnings, tostring(ev[4]))
end
if ev[1] == "k.timer" then
if gpu then
local bg = 0xFFFFFF
pcall(gpu.setForeground, 0x000000)
if w < stepCount then
local n = math.floor((w / stepCount) * 255)
bg = (n * 0x10000) + (n * 0x100) + n
pcall(gpu.setBackground, (n * 0x10000) + (n * 0x100) + n)
else
pcall(gpu.setBackground, 0xFFFFFF)
end
basicDraw(bg)
basicDraw(gpu)
end
if steps[w] then
if steps[w] == "INJECT" then
local nsm = neo.requestAccess("x.neo.sys.manage")
if not nsm then
table.insert(console, "Settings not available for INJECT.")
table.insert(warnings, "Settings not available for INJECT.")
else
local nextstepsA = {}
local nextstepsB = {}
@ -407,8 +412,10 @@ local function initializeSystem()
else
local v, err = neo.executeAsync(steps[w])
if not v then
neo.emergency("failed start:", steps[w])
neo.emergency(steps[w] .. " STF")
neo.emergency(err)
table.insert(warnings, steps[w] .. " STF")
table.insert(warnings, err)
end
end
else
@ -430,10 +437,12 @@ end
-- System initialized
if finalPrompt() then
-- Safe Mode
local gpu = gpuG()
rstfbDraw(gpu)
basicDraw(gpu)
local nsm = neo.requestAccess("x.neo.sys.manage")
if nsm then
console = {"Rebooting for Safe Mode..."}
basicDraw()
pcall(gpu.set, 2, 4, "Rebooting for Safe Mode...")
for _, v in ipairs(nsm.listSettings()) do
if v ~= "password" then
nsm.delSetting(v)
@ -441,14 +450,12 @@ if finalPrompt() then
end
else
-- assume sysconf.lua did something very bad
console = {"No NSM. Wiping configuration completely."}
pcall(gpu.set, 2, 4, "No NSM. Wiping configuration completely.")
local fs = neo.requestAccess("c.filesystem")
if not fs then
table.insert(console, "Failed to get permission, you're doomed.")
else
fs.primary.remove("/data/sys-glacier/sysconf.lua")
pcall(gpu.set, 2, 4, "Failed to get permission, you're doomed.")
end
basicDraw()
fs.primary.remove("/data/sys-glacier/sysconf.lua")
end
-- Do not give anything a chance to alter the new configuration
shutdownEmergency(true)

View File

@ -1,14 +1,9 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
return {
["neo"] = {
desc = "KittenOS NEO Kernel & Base Libs",
v = 10,
v = 4,
deps = {
},
dirs = {
@ -23,7 +18,6 @@ return {
"libs/serial.lua",
"libs/fmttext.lua",
"libs/neoux.lua",
"libs/lineedit.lua",
"libs/braille.lua",
"libs/bmp.lua",
"libs/sys-filewrap.lua",
@ -32,7 +26,7 @@ return {
},
["neo-init"] = {
desc = "KittenOS NEO / sys-init (startup)",
v = 11,
v = 2,
deps = {
"neo",
"neo-icecap",
@ -47,7 +41,7 @@ return {
},
["neo-launcher"] = {
desc = "KittenOS NEO / Default app-launcher",
v = 10,
v = 2,
deps = {
"neo"
},
@ -60,7 +54,7 @@ return {
},
["neo-everest"] = {
desc = "KittenOS NEO / Everest (windowing)",
v = 10,
v = 3,
deps = {
"neo"
},
@ -73,7 +67,7 @@ return {
},
["neo-icecap"] = {
desc = "KittenOS NEO / Icecap",
v = 10,
v = 3,
deps = {
"neo"
},
@ -90,7 +84,7 @@ return {
},
["neo-secpolicy"] = {
desc = "KittenOS NEO / Secpolicy",
v = 10,
v = 2,
deps = {
},
dirs = {
@ -102,7 +96,7 @@ return {
},
["neo-coreapps"] = {
desc = "KittenOS NEO Core Apps",
v = 10,
v = 2,
deps = {
"neo"
},
@ -111,39 +105,40 @@ return {
},
files = {
"apps/app-textedit.lua",
"apps/app-batmon.lua",
"apps/app-control.lua",
"apps/app-taskmgr.lua"
}
},
["app-bmpview"] = {
desc = "KittenOS NEO .bmp viewer",
v = 10,
["app-klogo"] = {
desc = "KittenOS NEO Logo shower",
v = 2,
deps = {
"neo",
"app-klogo-logo"
},
dirs = {
"apps"
},
files = {
"apps/app-bmpview.lua",
"apps/app-klogo.lua",
},
},
["neo-logo"] = {
["app-klogo-logo"] = {
desc = "KittenOS NEO Logo (data)",
v = 10,
v = 4,
deps = {
},
dirs = {
"docs"
"data",
"data/app-klogo"
},
files = {
"docs/logo.bmp"
"data/app-klogo/logo.bmp"
},
},
["app-flash"] = {
desc = "KittenOS NEO EEPROM Flasher",
v = 10,
v = 2,
deps = {
"neo"
},
@ -156,7 +151,7 @@ return {
},
["app-wget"] = {
desc = "KittenOS Web Retriever",
v = 10,
v = 2,
deps = {
"neo"
},
@ -169,35 +164,23 @@ return {
},
["app-claw"] = {
desc = "KittenOS NEO Package Manager",
v = 10,
v = 2,
deps = {
"neo"
},
dirs = {
"apps"
"apps",
"libs"
},
files = {
"apps/app-claw.lua",
"apps/svc-app-claw-worker.lua"
},
},
["svc-t"] = {
desc = "KittenOS NEO Terminal System",
v = 10,
deps = {
"neo"
},
dirs = {
"apps"
},
files = {
"apps/svc-t.lua",
"apps/app-luashell.lua"
"libs/app-claw-core.lua",
"libs/app-claw-csi.lua"
},
},
["neo-meta"] = {
desc = "KittenOS NEO: Use 'All' to install to other disks",
v = 10,
v = 2,
deps = {
"neo",
"neo-init",
@ -206,11 +189,9 @@ return {
"neo-icecap",
"neo-secpolicy",
"neo-coreapps",
"neo-logo",
"app-bmpview",
"app-klogo",
"app-flash",
"app-claw",
"svc-t",
"app-wget"
},
dirs = {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,10 +1,6 @@
-- KittenOS N.E.O Kernel: "Tell Mettaton I said hi."
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- NOTE: local is considered unnecessary in kernel since 21 March
@ -463,7 +459,7 @@ function retrieveAccess(perm, pkg, pid)
accesses[uid] = function (pkg, pid)
return nil
end
return function (f, secret)
return function (f)
-- Registration function
ensureType(f, "function")
local accessObjectCache = {}
@ -485,10 +481,8 @@ function retrieveAccess(perm, pkg, pid)
end
-- returns nil and fails
end
if not secret then
-- Announce registration
distEvent(nil, "k.registration", uid)
end
-- Announce registration
distEvent(nil, "k.registration", uid)
end, function ()
-- Registration becomes null (access is held but other processes cannot retrieve object)
if accesses[uid] then

254
code/libs/app-claw-core.lua Normal file
View File

@ -0,0 +1,254 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-claw-core: assistant to app-claw
-- should only ever be one app-claw at a time
-- USING THIS LIBRARY OUTSIDE OF APP-CLAW IS A BAD IDEA.
-- SO DON'T DO IT.
-- Also serves to provide a mutex.
local lock = false
return function ()
if lock then
error("libclaw safety lock in use")
end
lock = true
local sourceList = {}
local sources = {}
-- 1 2 3 4 5 6
-- source ents: src dst index
-- dst entries: writeFile(fn), mkdir(fn), exists(fn), isDirectory(fn), remove(fn), rename(fna, fnb)
-- writeFile(fn) -> function (data/nil to close)
local function saveInfo(dn)
sources[dn][2][2]("data")
sources[dn][2][2]("data/app-claw")
local cb, _, r = sources[dn][2][1]("data/app-claw/local.lua")
if not cb then return false, r end
_, r = cb(require("serial").serialize(sources[dn][3]))
if not _ then return false, r end
_, r = cb(nil)
if not _ then return false, r end
return true
end
local remove, installTo, expandCSI, compressCSI
local function expandS(v)
return v:sub(2, v:byte(1) + 1), v:sub(v:byte(1) + 2)
end
local function expandT(v)
local t = {}
local n = v:byte(1)
v = v:sub(2)
for i = 1, n do
t[i], v = expandS(v)
end
return t, v
end
local function compressT(x)
local b = string.char(#x)
for _, v in ipairs(x) do
b = b .. string.char(#v) .. v
end
return b
end
local function expandCSI(v)
local t = {}
local k
k, v = expandS(v)
t.desc, v = expandS(v)
t.v = (v:byte(1) * 256) + v:byte(2)
v = v:sub(3)
t.dirs, v = expandT(v)
t.files, v = expandT(v)
t.deps, v = expandT(v)
return k, t, v
end
local function compressCSI(k, v)
local nifo = string.char(#k) .. k
nifo = nifo .. string.char(math.min(255, #v.desc)) .. v.desc:sub(1, 255)
nifo = nifo .. string.char(math.floor(v.v / 256), v.v % 256)
nifo = nifo .. compressT(v.dirs)
nifo = nifo .. compressT(v.files)
nifo = nifo .. compressT(v.deps)
return nifo
end
local function findPkg(idx, pkg, del)
del = del and ""
idx = sources[idx][3]
while #idx > 0 do
local k, d
k, d, idx = expandCSI(idx)
if del then
if k == pkg then
return d, del .. idx
end
del = del .. compressCSI(k, d)
else
if k == pkg then return d end
end
end
end
-- NOTE: Functions in this must return something due to the checked-call wrapper,
-- but should all use error() for consistency.
-- Operations
installTo = function (dstName, pkg, srcName, checked, yielder)
local errs = {}
if srcName == dstName then
error("Invalid API use")
end
-- preliminary checks
local srcPkg = findPkg(srcName, pkg, false)
assert(srcPkg)
if checked then
for _, v in ipairs(srcPkg.deps) do
if not findPkg(dstName, v) then
if not findPkg(srcName, v) then
table.insert(errs, pkg .. " depends on " .. v .. "\n")
elseif #errs == 0 then
installTo(dstName, v, srcName, checked, yielder)
else
table.insert(errs, pkg .. " depends on " .. v .. " (can autoinstall)\n")
end
end
end
end
-- Files from previous versions to get rid of
local ignFiles = {}
local oldDst = findPkg(dstName, pkg)
if oldDst then
for _, v in ipairs(oldDst.files) do
ignFiles[v] = true
end
end
oldDst = nil
for _, v in ipairs(srcPkg.files) do
if not ignFiles[v] then
if sources[dstName][2][3](v) then
table.insert(errs, v .. " already exists (corrupt system?)")
end
end
end
if #errs > 0 then
error(table.concat(errs))
end
for _, v in ipairs(srcPkg.dirs) do
sources[dstName][2][2](v)
if not sources[dstName][2][4](v) then
error("Unable to create directory " .. v)
end
end
for _, v in ipairs(srcPkg.files) do
local tmpOut, r, ok = sources[dstName][2][1](v .. ".claw-tmp")
ok = tmpOut
if ok then
ok, r = sources[srcName][1](v, tmpOut)
end
if ok then
yielder()
else
-- Cleanup...
for _, v in ipairs(srcPkg.files) do
sources[dstName][2][5](v .. ".claw-tmp")
end
error(r)
end
end
-- PAST THIS POINT, ERRORS CORRUPT!
-- Remove package from DB
local oldDst2, oldDst3 = findPkg(dstName, pkg, true)
sources[dstName][3] = oldDst3 or sources[dstName][3]
oldDst2, oldDst3 = nil
saveInfo(dstName)
-- Delete old files
for k, _ in pairs(ignFiles) do
yielder()
sources[dstName][2][5](k)
end
-- Create new files
for _, v in ipairs(srcPkg.files) do
yielder()
sources[dstName][2][6](v .. ".claw-tmp", v)
end
-- Insert into DB
sources[dstName][3] = sources[dstName][3] .. compressCSI(pkg, srcPkg)
saveInfo(dstName)
return true
end
remove = function (dstName, pkg, checked)
if checked then
local errs = {}
local buf = sources[dstName][3]
while #buf > 0 do
local dpsName, dpsV
dpsName, dpsV, buf = expandCSI(buf)
for _, v in ipairs(dpsV.deps) do
if v == pkg then
table.insert(errs, dpsName .. " depends on " .. pkg .. "\n")
end
end
end
if #errs > 0 then
return nil, table.concat(errs)
end
end
local dstPkg, nbuf = findPkg(dstName, pkg, true)
assert(dstPkg, "Package wasn't installed")
for _, v in ipairs(dstPkg.files) do
sources[dstName][2][5](v)
end
sources[dstName][3] = nbuf
saveInfo(dstName)
return true
end
return {
-- Gets the latest info, or if given a source just gives that source's info.
-- Do not modify output.
getInfo = function (pkg, source, oldest)
if source then return findPkg(source, pkg) end
local bestI = {
v = -1,
desc = "An unknown package.",
deps = {}
}
if oldest then bestI.v = 10000 end
for k, v in pairs(sources) do
local pkgv = findPkg(k, pkg)
if pkgv then
if ((not oldest) and (pkgv.v > bestI.v)) or (oldest and (pkgv.v > bestI.v)) then
bestI = pkgv
end
end
end
return bestI
end,
sources = sources,
sourceList = sourceList,
remove = remove,
installTo = installTo,
expandCSI = expandCSI,
compressCSI = compressCSI,
-- Gets a total list of packages, as a table of strings. You can modify output.
getList = function ()
local n = {}
local seen = {}
for k, v in pairs(sources) do
local p3 = v[3]
while #p3 > 0 do
local kb, _, nx = expandCSI(p3)
p3 = nx
if not seen[kb] then
seen[kb] = true
table.insert(n, kb)
end
end
end
table.sort(n)
return n
end,
unlock = function ()
lock = false
end
}
end

View File

@ -0,0 +1,28 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-claw-csi: addSource function
-- USING THIS LIBRARY OUTSIDE OF APP-CLAW IS A BAD IDEA.
-- SO DON'T DO IT.
-- NOTE: If a source is writable, it's added anyway despite any problems.
return function (claw, name, src, dst)
local ifo = ""
local ifok, e = src("data/app-claw/local.lua", function (t)
ifo = ifo .. (t or "")
return true
end)
e = e or "local.lua parse error"
ifo = ifok and require("serial").deserialize(ifo)
if not (dst or ifo) then return false, e end
table.insert(claw.sourceList, {name, not not dst})
local nifo = ifo or ""
if type(nifo) == "table" then
nifo = ""
for k, v in pairs(ifo) do
nifo = nifo .. claw.compressCSI(k, v)
end
end
claw.sources[name] = {src, dst, nifo}
return not not ifo, e
end

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- bmp: Portable OC BMP/DIB library
-- Flexible: Reading can be set to
@ -157,8 +153,20 @@ return {
local planes = get16(0x1A)
local bpp = get16(0x1C)
local compression = get32(0x1E)
local paletteCol = get32(0x2E)
local paletteCol = 0
local other = get32(0x2E)
paletteCol = other
-- postprocess
-- The actual values used for addressing, for cMode to mess with
local basePtr = 14 + hdrSize + (paletteCol * 4)
local scanWB = math.ceil((bpp * width) / 32) * 4
local monoWB = (math.ceil((bpp * width) / 32) * 4)
local planeWB = scanWB * height
if not packed then
basePtr = get32(0x0A) -- 'BM' header
end
-- negative height means sane coords
local upDown = true
if height >= 0x80000000 then
@ -167,18 +175,6 @@ return {
upDown = false
end
-- postprocess
-- The actual values used for addressing, for cMode to mess with
local basePtr = 14 + hdrSize + (paletteCol * 4)
local scanWB = math.ceil((bpp * width) / 32) * 4
local monoWB = math.ceil(width / 32) * 4
local planeWB = scanWB * height
if not packed then
basePtr = get32(0x0A) -- 'BM' header
end
-- Cursor/Icon
if cMode then
height = math.floor(height / 2)

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- Braille Converter & NeoUX component
-- While the Neoux part isn't OS-independent,

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- event: Implements pull/listen semantics in a consistent way for a given process.
-- This is similar in API to OpenOS's event framework, but is per-process.

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
local fmt
fmt = {

View File

@ -1,77 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
return {
-- note: everything must already be unicode.safeTextFormat'd
draw = function (sW, line, cursorX, rX)
-- if no camera, provide a default
rX = rX or math.max(1, (cursorX or 1) - math.floor(sW * 2 / 3))
-- transform into area-relative
local tl = unicode.sub(line, rX, rX + sW - 1)
-- inject cursor
if cursorX then
cursorX = (cursorX - rX) + 1
if cursorX >= 1 then
if cursorX <= sW then
tl = unicode.sub(tl, 1, cursorX - 1) .. "_" .. unicode.sub(tl, cursorX + 1)
end
end
end
return tl .. (" "):rep(sW - unicode.len(tl))
end,
clamp = function (tl, cursorX)
tl = unicode.len(tl)
if tl < cursorX - 1 then
return tl + 1
end
return cursorX
end,
-- returns line, cursorX, special
-- return values may be nil if irrelevant
key = function (ks, kc, line, cursorX)
local cS = unicode.sub(line, 1, cursorX - 1)
local cE = unicode.sub(line, cursorX)
local ll = unicode.len(line)
if kc == 203 then -- navi <
if cursorX > 1 then
return nil, cursorX - 1
else
-- cline underflow
return nil, nil, "l<"
end
elseif kc == 205 then -- navi >
if cursorX > ll then
-- cline overflow
return nil, nil, "l>"
end
return nil, cursorX + 1
elseif kc == 199 then -- home
return nil, 1
elseif kc == 207 then -- end
return nil, unicode.len(line) + 1
elseif ks == "\8" then -- del
if cursorX == 1 then
-- weld prev
return nil, nil, "w<"
else
return unicode.sub(cS, 1, unicode.len(cS) - 1) .. cE, cursorX - 1
end
elseif kc == 211 then -- del
if cursorX > ll then
return nil, nil, "w>"
end
return cS .. unicode.sub(cE, 2)
elseif ks then -- standard letters
if ks == "\r" then
-- new line
return nil, nil, "nl"
end
return cS .. ks .. cE, cursorX + unicode.len(ks)
end
-- :(
end
}

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- neoux: Implements utilities on top of Everest & event:
-- Everest crash protection
@ -29,7 +25,7 @@ newNeoux = function (event, neo)
end
end)
local neoux = {}
neoux.fileDialog = function (forWrite, callback, dfn)
neoux.fileDialog = function (forWrite, callback)
local sync = false
local rtt = nil
if not callback then
@ -39,7 +35,7 @@ newNeoux = function (event, neo)
rtt = rt
end
end
local tag = neo.requireAccess("x.neo.pub.base", "filedialog").showFileDialogAsync(forWrite, dfn)
local tag = neo.requireAccess("x.neo.pub.base", "filedialog").showFileDialogAsync(forWrite)
local f
f = function (_, fd, tg, re)
if fd == "filedialog" then
@ -343,8 +339,6 @@ newNeoux = function (event, neo)
end
-- Note: w should be at least 2 - this is similar to buttons.
neoux.tcfield = function (x, y, w, textprop)
-- compat. workaround for apps which nuke tcfields
local p = unicode.len(textprop()) + 1
return {
x = x,
y = y,
@ -353,24 +347,22 @@ newNeoux = function (event, neo)
selectable = true,
key = function (window, update, a, c, d, f)
if d then
local ot = textprop()
local le = require("lineedit")
p = le.clamp(ot, p)
if c == 63 then
neo.requireAccess("x.neo.pub.globals", "clipboard").setSetting("clipboard", ot)
neo.requireAccess("x.neo.pub.globals", "clipboard").setSetting("clipboard", textprop())
elseif c == 64 then
local contents = neo.requireAccess("x.neo.pub.globals", "clipboard").getSetting("clipboard")
contents = contents:match("^[^\r\n]*")
textprop(contents)
update()
elseif a ~= 9 then
local lT, lC, lX = le.key(a ~= 0 and unicode.char(a), c, ot, p)
if lT or lC then
if lT then textprop(lT) end
p = lC or p
update()
return true
end
elseif a == 8 then
local str = textprop()
textprop(unicode.sub(str, 1, unicode.len(str) - 1))
update()
return true
elseif a >= 32 then
textprop(textprop() .. unicode.char(a))
update()
return true
end
end
end,
@ -385,10 +377,9 @@ newNeoux = function (event, neo)
fg = bg
bg = fg1
end
local t, e, r = textprop(), require("lineedit")
p = e.clamp(t, p)
t, r = unicode.safeTextFormat(t, p)
window.span(x, y, "[" .. e.draw(w - 2, t, selected and r) .. "]", bg, fg)
local text = unicode.safeTextFormat(textprop())
text = "[" .. neoux.pad(text, w - 2, false, true, true) .. "]"
window.span(x, y, text, bg, fg)
end
}
end

View File

@ -1,10 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
local doSerialize = nil
function doSerialize(s)
if type(s) == "table" then

View File

@ -1,12 +1,8 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- just don't bother with proper indent here
return function (event, nexus, retFunc, fs, pkg, mode, defName)
return function (event, nexus, retFunc, fs, pkg, mode)
local fmt = require("fmttext")
local class = "manage"
@ -145,7 +141,7 @@ end
nexus.create(w, h, class .. " " .. pkg, function (w, ev, a, b, c)
if not wnd then
wnd = w
prepareNode(require("sys-filevfs")(fs, mode, defName))
prepareNode(require("sys-filevfs")(fs, mode))
end
if ev == "key" then
key2(a, b, c)

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- Used by filedialog to provide a sane relative environment.
-- Essentially, the filedialog is just a 'thin' UI wrapper over this.
@ -20,12 +16,12 @@ end
local getFsNode, getRoot
local setupCopyNode
function setupCopyNode(parent, myRoot, op, complete)
function setupCopyNode(parent, myRoot, op, complete, impliedName)
local function handleResult(aRes, res)
if aRes then
return complete(res, true)
else
return nil, setupCopyNode(parent, res, op, complete)
return nil, setupCopyNode(parent, res, op, complete, impliedName)
end
end
return {
@ -36,6 +32,11 @@ function setupCopyNode(parent, myRoot, op, complete)
complete(nil, false)
return false, parent
end})
if impliedName and myRoot.unknownAvailable then
table.insert(l, {"Implied: " .. impliedName, function ()
return handleResult(myRoot.selectUnknown(impliedName))
end})
end
for _, v in ipairs(myRoot.list()) do
table.insert(l, {v[1], function ()
return handleResult(v[2]())
@ -53,7 +54,7 @@ local function setupCopyVirtualEnvironment(fs, parent, fwrap, impliedName)
if not fwrap then
return false, dialog("Could not open source", parent)
end
local myRoot = getRoot(fs, true, impliedName)
local myRoot = getRoot(fs, true)
-- Setup wrapping node
return setupCopyNode(parent, myRoot, "Copy", function (fwrap2, intent)
if not fwrap2 then
@ -71,22 +72,14 @@ local function setupCopyVirtualEnvironment(fs, parent, fwrap, impliedName)
fwrap.close()
fwrap2.close()
return false, dialog("Completed copy.", parent)
end)
end, impliedName)
end
function getFsNode(fs, parent, fsc, path, mode, impliedName)
function getFsNode(fs, parent, fsc, path, mode)
local va = fsc.address:sub(1, 4)
local fscrw = not fsc.isReadOnly()
local dir = path:sub(#path, #path) == "/"
local confirmedDel = false
local t
local function selectUnknown(text)
-- Relies on text being nil if used in leaf node
local rt, re = require("sys-filewrap").create(fsc, path .. (text or ""), mode)
if not rt then
return false, dialog("Open Error: " .. tostring(re), parent)
end
return true, rt
end
t = {
name = ((dir and "DIR: ") or "FILE: ") .. va .. path,
list = function ()
@ -98,25 +91,11 @@ function getFsNode(fs, parent, fsc, path, mode, impliedName)
for k, v in ipairs(fsc.list(path)) do
local nm = "F: " .. v
local fp = path .. v
local cDir = fsc.isDirectory(fp)
if cDir then
if fsc.isDirectory(fp) then
nm = "D: " .. v
end
if (not cDir) and (fscrw or mode == false) and (mode ~= nil) then
local vn = v
n[k + 1] = {nm, function () return selectUnknown(vn) end}
else
n[k + 1] = {nm, function () return nil, getFsNode(fs, t, fsc, fp, mode, impliedName) end}
end
n[k + 1] = {nm, function () return nil, getFsNode(fs, t, fsc, fp, mode) end}
end
else
table.insert(n, {"Copy", function ()
local rt, re = require("sys-filewrap").create(fsc, path, false)
if not rt then
return false, dialog("Open Error: " .. tostring(re), parent)
end
return nil, setupCopyVirtualEnvironment(fs, parent, rt, path:match("[^/]*$") or "")
end})
end
if fscrw then
if dir then
@ -135,6 +114,31 @@ function getFsNode(fs, parent, fsc, path, mode, impliedName)
end
}
end})
else
if mode ~= nil then
local tx = "Open"
if mode == true then
tx = "Save"
elseif mode == "append" then
tx = "Append"
end
if fscrw or mode == false then
table.insert(n, {tx, function ()
local rt, re = require("sys-filewrap").create(fsc, path, mode)
if not rt then
return false, dialog("Open Error: " .. tostring(re), parent)
end
return true, rt
end})
end
end
table.insert(n, {"Copy", function ()
local rt, re = require("sys-filewrap").create(fsc, path, false)
if not rt then
return false, dialog("Open Error: " .. tostring(re), parent)
end
return nil, setupCopyVirtualEnvironment(fs, parent, rt, path:match("[^/]*$") or "")
end})
end
if path ~= "/" then
local delText = "Delete"
@ -168,20 +172,20 @@ function getFsNode(fs, parent, fsc, path, mode, impliedName)
end})
end
end
if not dir then
elseif impliedName then
table.insert(n, {"Implied: " .. impliedName, function ()
return selectUnknown(impliedName)
end})
end
return n
end,
unknownAvailable = dir and (mode ~= nil) and ((mode == false) or fscrw),
selectUnknown = selectUnknown
selectUnknown = function (text)
local rt, re = require("sys-filewrap").create(fsc, path .. text, mode)
if not rt then
return false, dialog("Open Error: " .. tostring(re), parent)
end
return true, rt
end
}
return t
end
function getRoot(fs, mode, defName)
function getRoot(fs, mode)
local t
t = {
name = "DRVS:",
@ -205,7 +209,7 @@ function getRoot(fs, mode, defName)
id = "RW " .. amount .. "% " .. mb .. "M " .. id
end
table.insert(l, {fsi.address:sub(1, 4) .. " " .. id, function ()
return nil, getFsNode(fs, t, fsi, "/", mode, defName)
return nil, getFsNode(fs, t, fsi, "/", mode)
end})
end
return l

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
local function ensureMode(mode)
local n = "rb"

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
return function (
gpus, screens,
@ -99,7 +95,7 @@ return function (
return v, didBind
end
end
end, monitor
end, v
end
end
end

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- CRITICAL FILE!
-- This file defines how your KittenOS NEO system responds to access requests.
@ -15,28 +11,21 @@
-- IRC is usually pretty safe, but no guarantees.
-- Returns "allow", "deny", or "ask".
local function actualPolicy(pkg, pid, perm, pkgSvcPfx)
local function actualPolicy(pkg, pid, perm, matchesSvc)
-- System stuff is allowed.
if pkg:sub(1, 4) == "sys-" then
return "allow"
end
-- svc-t's job is solely to emulate terminals
-- TO INSTALL YOUR OWN TERMINAL EMULATOR:
-- perm|app-yourterm|r.neo.t
if pkg == "svc-t" and perm == "r.neo.pub.t" then
return "allow"
end
-- <The following is for apps & services>
-- x.neo.pub.* is open to all
-- x.neo.pub (aka Icecap) is open to all
if perm:sub(1, 10) == "x.neo.pub." then
return "allow"
end
-- These signals are harmless, though they identify HW (as does everything in OC...)
if perm == "s.h.component_added" or perm == "s.h.component_removed" or perm == "s.h.tablet_use" or perm == "c.tablet" then
if perm == "s.h.component_added" or perm == "s.h.component_removed" then
return "allow"
end
-- Userlevel can register for itself
if perm == "r." .. pkgSvcPfx then
if matchesSvc("r.", pkg, perm) then
return "allow"
end
-- Userlevel has no other registration rights
@ -55,8 +44,8 @@ local function actualPolicy(pkg, pid, perm, pkgSvcPfx)
return "ask"
end
return function (nexus, settings, pkg, pid, perm, rsp, pkgSvcPfx)
local res = actualPolicy(pkg, pid, perm, pkgSvcPfx)
return function (nexus, settings, pkg, pid, perm, rsp, matchesSvc)
local res = actualPolicy(pkg, pid, perm, matchesSvc)
if settings then
res = settings.getSetting("perm|" .. pkg .. "|" .. perm) or
settings.getSetting("perm|*|" .. perm) or res

61
com2/bdivide.lua Normal file
View File

@ -0,0 +1,61 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- BDIVIDE
-- format:
-- 0-127 for constants
-- <block + 128>, <(len - 3) * 2, + lowest bit is upper bit of position>, <position - 1>
io.write("\x00") -- initiation character
local blockCache = {}
local window = 0
local blockUse = 128
for i = 128, 128 + blockUse - 1 do
blockCache[i] = ("\x00"):rep(512)
end
local function runBlock(blk)
-- firstly, get current block index
local blockIndex = window + 128
window = (window + 1) % blockUse
blockCache[blockIndex] = ""
-- ok, now work on the problem
local i = 1
while i <= #blk do
local bestData = blk:sub(i, i)
local bestRes = bestData
local bestScore = 1
for bid = 128, 128 + blockUse - 1 do
for lm = 0, 127 do
local al = lm + 3
local pfx = blk:sub(i, i + al - 1)
if #pfx ~= al then
break
end
local p = blockCache[bid]:find(pfx, 1, true)
if not p then
break
end
local score = al / 3
if score > bestScore then
bestData = string.char(bid) .. string.char((lm * 2) + math.floor((p - 1) / 256)) .. string.char((p - 1) % 256)
bestRes = pfx
bestScore = score
end
end
end
-- ok, encode!
io.write(bestData)
blockCache[blockIndex] = blockCache[blockIndex] .. bestRes
i = i + #bestRes
end
end
while 1 do
local blkd = io.read(512)
runBlock(blkd)
if #blkd < 512 then
return
end
end

73
com2/bundiv.lua Normal file
View File

@ -0,0 +1,73 @@
-- This is released into the public domain. XX
-- No warranty is provided, implied or otherwise. XX
local sector = io.write -- XX
-- BUNDIVIDE reference implementation for integration XX
local Cs,Cbu,Cb,Cw,Cp,Ct,Ci,CP,CB,CD={},128,"",128,"",""
CP = function(d,b)
Ct = Ct .. d
while#Ct>2 do
b = Ct:byte()
Ct = Ct:sub(2)
if b == 127 then
b = Ct:byte()
Ct = Ct:sub(2)
if b == 127 then
b = Ct:byte()+254
if b > 255then
b = b - 256
end
Ct = Ct:sub(2)
else
b = b + 127
end
end
Cp = Cp..string.char(b)
if #Cp == 512 then
sector(Cp)
Cp = ""
end
end
end
for i = 128, 127 + Cbu do
Cs[i] = ("\x00"):rep(512)
end
Cs[Cw] = ""
CB = function (d, i, d2, x, y)
i=1
while i <= #d - 2 do
b=d:byte(i)
d2=d:sub(i,i)
i=i+1
if not Ci then
if b==0then
Ci=1
end
else
if b >= 128 then
x = d:byte(i)
i = i + 1
y=d:byte(i) + ((x % 2) * 256)
i = i + 1
d2=Cs[b]:sub(y + 1,y + 3 + math.floor(x / 2))
end
Cs[Cw]=Cs[Cw]..d2
if #Cs[Cw]>=512 then
CP(Cs[Cw])
Cw=((Cw-127)%Cbu)+128
Cs[Cw]=""
end
end
end
return i
end
CD = function(d)
Cb = Cb .. d
Cb = Cb:sub(CB(Cb))
end
CD(io.read("*a")) -- XX
--D.remove("init-bdivide.lua")--
--D.rename("init.lua","init-bdivide.lua")--
--local Ch=D.open("init-bdivide.lua","rb")--
--dieCB=function()D.close(Ch)D.remove("init-bdivide.lua")end--
--while true do local t=D.read(Ch, 64)if not t then break end CD(t)end--
CD("\x00\x00")CP(Cs[Cw] .. "\x00\x00")

20
com2/preproc.lua Normal file
View File

@ -0,0 +1,20 @@
-- PREPROC: preprocess input to be 7-bit
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
while true do
local c = io.read(1)
if not c then return end
local bc = c:byte()
if bc < 127 then
io.write(c)
else
if bc <= 253 then
-- 127(0) through 253(126)
io.write("\x7F" .. string.char(bc - 127))
else
-- 254(0) through 255 (1)
io.write("\x7F\x7F" .. string.char(bc - 254))
end
end
end

View File

@ -1,11 +1,4 @@
-- KittenOS NEO Repository Compliance Check Tool
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- I'm still not a lawyer
local filesAccountedFor = {
["repository/data/app-claw/local.lua"] = 0,

27
heroes.lua Normal file
View File

@ -0,0 +1,27 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- arg is the size of the code.tar file
local arg = ...
os.execute("lua com2/preproc.lua < code.tar | lua com2/bdivide.lua > com2/code.tar.bd")
os.execute("cat insthead.lua")
print("sC=" .. math.ceil(tonumber(arg) / 512))
local src = io.open("com2/bundiv.lua", "r")
while true do
local line = src:read()
if not line then
io.flush()
src:close()
return
end
local endix = line:sub(#line-1,#line)
if endix ~= "XX" then
if endix == "--" then
-- included
print(line:sub(3,#line-2))
else
print(line)
end
end
-- XX means ignored.
end

34
imitclaw.lua Normal file
View File

@ -0,0 +1,34 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- Imitation CLAW
local done = {}
local f, e = loadfile("code/data/app-claw/local.lua")
if not f then error(e) end
f = f()
if not os.execute("mkdir work") then
error("Delete 'work'")
end
for k, v in pairs(f) do
for _, vd in ipairs(v.dirs) do
os.execute("mkdir work/" .. vd .. " 2> /dev/null")
end
for _, vf in ipairs(v.files) do
-- not totally proofed but will do
if not os.execute("cp code/" .. vf .. " work/" .. vf) then
error("Could not copy " .. vf .. " in " .. k)
end
if done[vf] then
error("duplicate " .. vf .. " in " .. k)
end
print(vf .. "\t\t" .. k)
done[vf] = true
end
end
os.execute("mkdir -p work/data/app-claw")
os.execute("cp code/data/app-claw/local.lua work/data/app-claw/local.lua")
os.execute("cd code ; find . > ../imitclaw.treecode")
os.execute("cd work ; find . > ../imitclaw.treework")
os.execute("diff -u imitclaw.treecode imitclaw.treework")
os.execute("rm imitclaw.treecode imitclaw.treework")

View File

@ -1,40 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
local bga = {}
local str = io.read("*a")
for i = 1, #str - 1 do
local bg = str:sub(i, i + 1)
bga[bg] = (bga[bg] or 0) + 1
end
local first = {}
local second = {}
local mode = ...
for k, v in pairs(bga) do
if mode == "combined" then
print(string.format("%08i: %02x%02x : %s", v, k:byte(1), k:byte(2), k))
end
first[k:sub(1, 1)] = (first[k:sub(1, 1)] or 0) + v
second[k:sub(1, 1)] = (second[k:sub(1, 1)] or 0) + v
end
for k, v in pairs(first) do
if mode == "first" then
print(string.format("%08i: %s", v, k))
end
end
for k, v in pairs(second) do
if mode == "second" then
print(string.format("%08i: %s", v, k))
end
end

View File

@ -1,89 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- PREPROC (r9 edition): preprocess input to be 7-bit
local frw = require("libs.frw")
local
-- SHARED WITH DECOMPRESSION ENGINE
function p(x, y)
if x == 126 then
return string.char(y), 3
elseif x == 127 then
return string.char(128 + y), 3
elseif x >= 32 then
return string.char(x), 2
elseif x == 31 then
return "\n", 2
elseif x == 30 then
return "\x00", 2
end
return string.char(("enart"):byte(x % 5 + 1), ("ndtelh"):byte((x - x % 5) / 5 + 1)), 2
end
local preprocParts = {}
local preprocMaxLen = 0
for i = 0, 127 do
for j = 0, 127 do
local d, l = p(i, j)
if d then
preprocMaxLen = math.max(preprocMaxLen, #d)
l = l - 1
if (not preprocParts[d]) or (#preprocParts[d] > l) then
if l == 2 then
preprocParts[d] = string.char(i, j)
else
preprocParts[d] = string.char(i)
end
end
end
end
end
local function preprocWithPadding(blk, p)
local out = ""
local needsPadding = false
while blk ~= "" do
p(blk)
local len = math.min(preprocMaxLen, #blk)
while len > 0 do
local seg = blk:sub(1, len)
if preprocParts[seg] then
out = out .. preprocParts[seg]
needsPadding = #preprocParts[seg] < 2
blk = blk:sub(#seg + 1)
break
end
len = len - 1
end
assert(len ~= 0)
end
-- This needsPadding bit is just sort of quickly added in
-- to keep this part properly maintained
-- even though it might never get used
if needsPadding then
return out .. "\x00"
end
return out
end
local bdCore = require("bdivide.core")
return function (data, lexCrunch)
io.stderr:write("preproc: ")
local pi = frw.progress()
local function p(b)
pi(1 - (#b / #data))
end
data = preprocWithPadding(data, p)
io.stderr:write("\nbdivide: ")
pi = frw.progress()
data = bdCore.bdividePad(bdCore.bdivide(data, p))
io.stderr:write("\n")
return lexCrunch.process(frw.read("bdivide/instdeco.lua"), {}), data
end

View File

@ -1,76 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- BDIVIDE r5 edition
-- Algorithm simplified for smaller implementation and potentially better compression
-- format:
-- 0-127 for constants
-- <128 + (length - 4)>, <position high>, <position low>
-- Position is where in the window it was found, minus 1.
-- windowSize must be the same between the encoder and decoder,
-- and is the amount of data preserved after cropping.
local bdivCore = {}
function bdivCore.bdivide(blk, p)
local out = ""
local windowSize = 0x10000
local windowData = ("\x00"):rep(windowSize)
while blk ~= "" do
p(blk)
local bestData = blk:sub(1, 1)
assert(blk:byte() < 128, "BDIVIDE does not handle 8-bit data")
local bestRes = bestData
for lm = 0, 127 do
local al = lm + 4
local pfx = blk:sub(1, al)
if #pfx ~= al then
break
end
local p = windowData:find(pfx, 1, true)
if not p then
break
end
local pm = p - 1
local thirdByte = pm % 256
bestData = string.char(128 + lm, math.floor(pm / 256), thirdByte)
bestRes = pfx
end
-- ok, encode!
out = out .. bestData
-- crop window
windowData = (windowData .. bestRes):sub(-windowSize)
blk = blk:sub(#bestRes + 1)
end
return out
end
-- Adds padding if required
function bdivCore.bdividePad(data)
local i = 1
-- Basically, if it ends on a literal,
-- then the literal won't get read without two padding bytes.
-- Otherwise (including if no data) it's fine.
local needsPadding = false
while i <= #data do
if data:byte(i) > 127 then
i = i + 3
needsPadding = false
else
i = i + 1
needsPadding = true
end
end
if needsPadding then
return data .. "\x00\x00"
end
return data
end
return bdivCore

View File

@ -1,68 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- BDIVIDE (r5 edition) and PREPROC (r9 edition)
-- decompression engine for installer
$bdPPBuffer = ""
$bdBDBuffer = ""
$bdBDWindow = ("\x00"):rep(2^16)
-- High-level breakdown:
-- q is unescaper.
-- It'll only begin to input if at least 3 bytes are available,
-- so you'll want to throw in 2 extra zeroes at the end of stream as done here.
-- It uses t (input buffer) and p (output buffer).
-- Ignore its second argument, as that's a lie, it's just there for a local.
-- L is the actual decompressor. It has the same quirk as q, wanting two more bytes.
-- It stores to c (compressed), and w (window).
-- It outputs that which goes to the window to q also.
-- And it also uses a fake local.
-- SEE compress.lua FOR THIS FUNCTION
function $bdPP(x, y)
if x == 126 then
return string.char(y), 3
elseif x == 127 then
return string.char(128 + y), 3
elseif x >= 32 then
return string.char(x), 2
elseif x == 31 then
return "\n", 2
elseif x == 30 then
return "\x00", 2
end
return string.char(("enart"):byte(x % 5 + 1), ("ndtelh"):byte((x - x % 5) / 5 + 1)), 2
end
${
function $engineInput($L|lData)
$bdBDBuffer = $bdBDBuffer .. $lData
while #$bdBDBuffer > 2 do
$lData = $bdBDBuffer:byte()
if $lData < 128 then
$lData = $bdBDBuffer:sub(1, 1)
$bdBDBuffer = $bdBDBuffer:sub(2)
else
${
$L|bdBDPtr = $bdBDBuffer:byte(2) * 256 + $bdBDBuffer:byte(3) + 1
$lData = $bdBDWindow:sub($bdBDPtr, $bdBDPtr + $lData - 125)
$bdBDBuffer = $bdBDBuffer:sub(4)
$}
end
$bdPPBuffer = $bdPPBuffer .. $lData
$bdBDWindow = ($bdBDWindow .. $lData):sub(-2^16)
while #$bdPPBuffer > 1 do
${
$lData, $L|bdPPAdv = $bdPP($bdPPBuffer:byte(), $bdPPBuffer:byte(2))
$bdPPBuffer = $bdPPBuffer:sub($bdPPAdv)
$}
$engineOutput($lData)
end
end
end
$}

View File

@ -1,22 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
local frw = require("libs.frw")
local bdCore = require("bdivide.core")
return function (data, lexCrunch)
io.stderr:write("\nbdivide: ")
local pi = frw.progress()
local function p(b)
pi(1 - (#b / #data))
end
data = bdCore.bdividePad(bdCore.bdivide(data, p))
io.stderr:write("\n")
return lexCrunch.process(frw.read("bdvlite/instdeco.lua"), {}), data
end

View File

@ -1,76 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- BDIVIDE r5 edition
-- Algorithm simplified for smaller implementation and potentially better compression
-- format:
-- 0-127 for constants
-- <128 + (length - 4)>, <position high>, <position low>
-- Position is where in the window it was found, minus 1.
-- windowSize must be the same between the encoder and decoder,
-- and is the amount of data preserved after cropping.
local bdivCore = {}
function bdivCore.bdivide(blk, p)
local out = ""
local windowSize = 0x10000
local windowData = ("\x00"):rep(windowSize)
while blk ~= "" do
p(blk)
local bestData = blk:sub(1, 1)
assert(blk:byte() < 128, "BDIVIDE does not handle 8-bit data")
local bestRes = bestData
for lm = 0, 127 do
local al = lm + 4
local pfx = blk:sub(1, al)
if #pfx ~= al then
break
end
local p = windowData:find(pfx, 1, true)
if not p then
break
end
local pm = p - 1
local thirdByte = pm % 256
bestData = string.char(128 + lm, math.floor(pm / 256), thirdByte)
bestRes = pfx
end
-- ok, encode!
out = out .. bestData
-- crop window
windowData = (windowData .. bestRes):sub(-windowSize)
blk = blk:sub(#bestRes + 1)
end
return out
end
-- Adds padding if required
function bdivCore.bdividePad(data)
local i = 1
-- Basically, if it ends on a literal,
-- then the literal won't get read without two padding bytes.
-- Otherwise (including if no data) it's fine.
local needsPadding = false
while i <= #data do
if data:byte(i) > 127 then
i = i + 3
needsPadding = false
else
i = i + 1
needsPadding = true
end
end
if needsPadding then
return data .. "\x00\x00"
end
return data
end
return bdivCore

View File

@ -1,34 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- BDIVIDE (r5 edition)
-- decompression engine used to decompress DEFLATE decompression engine
$bdBDBuffer = ""
$bdBDWindow = ("\x00"):rep(2^16)
${
function $engineInput($L|lData)
$bdBDBuffer = $bdBDBuffer .. $lData
while #$bdBDBuffer > 2 do
$lData = $bdBDBuffer:byte()
if $lData < 128 then
$lData = $bdBDBuffer:sub(1, 1)
$bdBDBuffer = $bdBDBuffer:sub(2)
else
${
$L|bdBDPtr = $bdBDBuffer:byte(2) * 256 + $bdBDBuffer:byte(3) + 1
$lData = $bdBDWindow:sub($bdBDPtr, $bdBDPtr + $lData - 125)
$bdBDBuffer = $bdBDBuffer:sub(4)
$}
end
$bdBDWindow = ($bdBDWindow .. $lData):sub(-2^16)
$engineOutput($lData)
end
end
$}

View File

@ -1,82 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- KittenOS NEO Installer Generator --
local args = {...}
local cid = args[1]
local tarName = args[2]
local algorithmsInReverseOrder = {}
for i = 3, #args do
table.insert(algorithmsInReverseOrder, 1, args[i])
end
local u = require("libs.frw")
local instSize = 0
local function put(data)
io.write(data)
instSize = instSize + #data
end
-- Installer Lexcrunch Context --
local lexCrunch = require("libs.lexcrunch")()
-- Installer Core --
-- installerFinalized:
-- Stuff that's already finished and put at the end of RISM. Prepend to this.
-- installerPayload / installerProgramLength:
-- The next-outer chunk that hasn't been written to the end of RISM
-- as the compression scheme (if one) has not been applied yet.
-- Really, installerProgramLength is only necessary because of the innermost chunk,
-- as that chunk has the TAR; additional data that's part of the same effective compression block,
-- but requires the program length to avoid it.
local installerPayload
local installerProgramLength
local installerFinalized = ""
do
local tarData = u.read(tarName)
local tarSectors = math.floor(#tarData / 512)
local installerCore = lexCrunch.process(u.read("instcore.lua"), {["$$SECTORS"] = tostring(tarSectors)})
installerPayload = installerCore .. tarData
installerProgramLength = #installerCore
end
-- Installer Compression --
for _, v in ipairs(algorithmsInReverseOrder) do
io.stderr:write("compressing (" .. v .. ")\n")
local algImpl = require(v .. ".compress")
local algEngine, algData = algImpl(installerPayload, lexCrunch)
io.stderr:write("result: " .. #installerPayload .. " -> " .. #algData .. "\n")
-- prepend the program length of the last section
algEngine = lexCrunch.process("$iBlockingLen = " .. installerProgramLength .. " " .. algEngine, {})
-- commit
installerPayload = algEngine
installerProgramLength = #installerPayload
installerFinalized = algData .. installerFinalized
end
-- Installer Final --
-- This is a special case, so the program length/payload/etc. business has to be repeated.
put("--" .. cid .. "\n")
put("--This is released into the public domain. No warranty is provided, implied or otherwise.\n")
put(lexCrunch.process(u.read("insthead.lua"), {["$$CORESIZE"] = tostring(installerProgramLength)}))
local RISM = installerPayload .. installerFinalized
RISM = RISM:gsub("\xFE", "\xFE\xFE")
RISM = RISM:gsub("]]", "]\xFE]")
RISM = "\x00" .. RISM
put("--[[" .. RISM .. "]]")
-- Dumping debug info --
local dbg = io.open("iSymTab", "wb")
lexCrunch.dump(dbg)
dbg:close()

View File

@ -1,18 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is a wrapper around (i.e. does not contain) Zopfli.
local frw = require("libs.frw")
return function (data, lexCrunch)
frw.write("tempData.bin", data)
os.execute("zopfli --i1 --deflate -c tempData.bin > tempZopf.bin")
local res = frw.read("tempZopf.bin")
os.execute("rm tempData.bin tempZopf.bin")
return lexCrunch.process(frw.read("deflate/instdeco.lua"), {}), res
end

View File

@ -1,291 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- THIS NEXT LINE IS CLEARLY AWFUL
$bdBDWindow = nil
-- THE DEFLATE DECOMPRESSOR OF MADNESS --
-- Core I/O --
-- $dfAlignToByteRemaining is
-- set to 8 in the outer engine
${
function $dfGetIntField($L|lLen, $L|lVal)
$lVal = 0
for $L|lI = 0, $lLen - 1 do
if coroutine.yield() then
$lVal = $lVal + 2^$lI
end
end
return $lVal
end
$}
-- Huffman Core --
-- The approach here is based around 1-prefixed integers.
-- These are stored in a flat table.
-- So 0b1000 is the pattern 000.
${
function $dfReadHuffmanSymbol($L|lTree, $L|lVal)
$lVal = 1
while not $lTree[$lVal] do
$lVal = ($lVal * 2) + $dfGetIntField(1)
end
return $lTree[$lVal]
end
$}
${
function $dfGenHuffmanTree($L|lCodeLens)
-- $L|lI (used everywhere; defining inside creates a bug because it gets globalized)
$L|lNextCode = {}
${
-- To explain:
-- lNextCode is needed all the way to the end.
-- But lBLCount isn't needed after it's used to
-- generate lNextCode.
-- And lCode is very, very temporary.
-- Hence this massive block.
$L|lBLCount = {}
-- Note the 0
for $lI = 0, 15 do
$lBLCount[$lI] = 0
end
for $lI = 0, #$lCodeLens do
${
$L|lCodeLen = $lCodeLens[$lI]
if $lCodeLen ~= 0 then
$lBLCount[$lCodeLen] = $lBLCount[$lCodeLen] + 1
end
$}
end
$L|lCode = 0
for $lI = 1, 15 do
$lCode = ($lCode + $lBLCount[$lI - 1]) * 2
$lNextCode[$lI] = $lCode
end
$}
$L|lTree = {}
for $lI = 0, #$lCodeLens do
${
$L|lCodeLen = $lCodeLens[$lI]
if $lCodeLen ~= 0 then
$L|lPow = math.floor(2 ^ $lCodeLen)
assert($lNextCode[$lCodeLen] < $lPow, "Tl" .. $lCodeLen)
$L|lK = $lNextCode[$lCodeLen] + $lPow
assert(not $lTree[$lK], "conflict @ " .. $lK)
$lTree[$lK] = $lI
$lNextCode[$lCodeLen] = $lNextCode[$lCodeLen] + 1
end
$}
end
return $lTree
end
$}
-- DEFLATE fixed trees --
${
$L|dfFixedTL = {}
for $L|lI = 0, 143 do $dfFixedTL[$lI] = 8 end
for $lI = 144, 255 do $dfFixedTL[$lI] = 9 end
for $lI = 256, 279 do $dfFixedTL[$lI] = 7 end
for $lI = 280, 287 do $dfFixedTL[$lI] = 8 end
$dfFixedLit = $dfGenHuffmanTree($dfFixedTL)
-- last line possibly destroyed dfFixedTL, but that's alright
$dfFixedTL = {}
for $lI = 0, 31 do $dfFixedTL[$lI] = 5 end
$dfFixedDst = $dfGenHuffmanTree($dfFixedTL)
$}
-- DEFLATE LZ Core --
$dfWindow = ("\x00"):rep(2^15)
$dfPushBuf = ""
${
function $dfOutput($L|lData)
$dfWindow = ($dfWindow .. $lData):sub(-2^15)
$dfPushBuf = $dfPushBuf .. $lData
end
$}
$dfBittblLength = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4,
5, 5, 5, 5, 0
}
$dfBasetblLength = {
3, 4, 5, 6, 7, 8, 9, 10,
11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115,
131, 163, 195, 227, 258
}
$dfBittblDist = {
0, 0, 0, 0, 1, 1, 2, 2,
3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13
}
$dfBasetblDist = {
1, 2, 3, 4, 5, 7, 9, 13,
17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073,
4097, 6145, 8193, 12289, 16385, 24577
}
${
function $dfReadBlockBody($L|lLit, $L|lDst, $L|lLitSym, $L|lLen, $L|lDCode, $L|lPtr)
while true do
$lLitSym = $dfReadHuffmanSymbol($lLit)
if $lLitSym <= 255 then
$dfOutput(string.char($lLitSym))
elseif $lLitSym == 256 then
return
elseif $lLitSym <= 285 then
$lLen = $dfBasetblLength[$lLitSym - 256] + $dfGetIntField($dfBittblLength[$lLitSym - 256])
$lDCode = $dfReadHuffmanSymbol($lDst)
$lPtr = 32769 - ($dfBasetblDist[$lDCode + 1] + $dfGetIntField($dfBittblDist[$lDCode + 1]))
for $L|lI = 1, $lLen do
$dfOutput($dfWindow:sub($lPtr, $lPtr))
end
else
error("nt" .. v)
end
end
end
$}
-- Huffman Dynamics --
${
function $dfReadHuffmanDynamicSubcodes($L|lDistLens, $L|lCount, $L|lMetatree, $L|lCode)
-- used a lot: $L|lI
$lCode = 0
$lDistLens[-1] = 0
while $lCode < $lCount do
-- use a tmpglb since it's just for the chain
$L|lInstr = $dfReadHuffmanSymbol($lMetatree)
if $lInstr < 16 then
$lDistLens[$lCode] = $lInstr
$lCode = $lCode + 1
elseif $lInstr == 16 then
for $lI = 1, 3 + $dfGetIntField(2) do
$lDistLens[$lCode] = $lDistLens[$lCode - 1]
$lCode = $lCode + 1
assert($lCode <= $lCount, "sc.over")
end
elseif $lInstr == 17 then
for $lI = 1, 3 + $dfGetIntField(3) do
$lDistLens[$lCode] = 0
$lCode = $lCode + 1
assert($lCode <= $lCount, "sc.over")
end
elseif $lInstr == 18 then
for $lI = 1, 11 + $dfGetIntField(7) do
$lDistLens[$lCode] = 0
$lCode = $lCode + 1
assert($lCode <= $lCount, "sc.over")
end
else
-- is this even possible?
error("sc.unki")
end
end
$lDistLens[-1] = nil
return $lDistLens
end
$}
$dfDynamicMetalensScramble = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
${
function $dfReadHuffmanDynamic($L|lState, $L|lLiteralSize, $L|lDistanceSize, $L|lDistanceLens)
-- $L|lI loop variable, used all over
-- to save on locals, this is reused
-- as metalens
$lState = {}
for $lI = 0, 18 do $lState[$lI] = 0 end
$lLiteralSize = $dfGetIntField(5) + 257
$lDistanceSize = $dfGetIntField(5) + 1
for $lI = 1, $dfGetIntField(4) + 4 do
$lState[$dfDynamicMetalensScramble[$lI]] = $dfGetIntField(3)
end
-- as metatree
$lState = $dfGenHuffmanTree($lState)
-- as concatenated subcodes
$lState = $dfReadHuffmanDynamicSubcodes({}, $lLiteralSize + $lDistanceSize, $lState)
-- The distance lengths are removed from lState,
-- while being added to lDistanceLens
-- The result is completion
$lDistanceLens = {}
for $lI = 0, $lDistanceSize - 1 do
$lDistanceLens[$lI] = $lState[$lLiteralSize + $lI]
$lState[$lLiteralSize + $lI] = nil
end
return $dfGenHuffmanTree($lState), $dfGenHuffmanTree($lDistanceLens)
end
$}
-- Main Thread --
${
$dfThread = coroutine.create(function ($L|lFinal, $L|lLitLen)
while true do
$lFinal = coroutine.yield()
$L|lBlockType = $dfGetIntField(2)
if $lBlockType == 0 then
-- literal
$dfGetIntField($dfAlignToByteRemaining)
$lLitLen = $dfGetIntField(16)
-- this is weird, ignore it
$dfGetIntField(16)
for $L|lI = 1, $lLitLen do
$dfOutput(string.char($dfGetIntField(8)))
end
elseif $lBlockType == 1 then
-- fixed Huffman
$dfReadBlockBody($dfFixedLit, $dfFixedDst)
elseif $lBlockType == 2 then
-- dynamic Huffman
$dfReadBlockBody($dfReadHuffmanDynamic())
else
error("b3")
end
while $lFinal do
coroutine.yield()
end
end
end)
$}
-- The Outer Engine --
coroutine.resume($dfThread)
${
function $engineInput($L|lData, $L|lByte)
for $L|lI = 1, #$lData do
$lByte = $lData:byte($lI)
$dfAlignToByteRemaining = 8
while $dfAlignToByteRemaining > 0 do
-- If we're providing the first bit (v = 8), then there are 7 bits remaining.
-- So this hits 0 when the *next* 8 yields will provide an as-is byte.
$dfAlignToByteRemaining = $dfAlignToByteRemaining - 1
assert(coroutine.resume($dfThread, $lByte % 2 == 1))
$lByte = math.floor($lByte / 2)
end
end
-- flush prepared buffer
$engineOutput($dfPushBuf)
$dfPushBuf = ""
end
$}

View File

@ -1,80 +0,0 @@
-- KOSNEO installer base
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
$icScreen = $component.list("screen", true)()
$icGPU = $component.list("gpu", true)()
$icFilename = "Starting..."
$icBytesRemaining = 0
if $icScreen and $icGPU then
$icGPU = $component.proxy($icGPU)
$icGPU.bind($icScreen)
$icGPU.setResolution(50, 5)
$icGPU.setBackground(2^24-1)
$icGPU.setForeground(0)
$icGPU.fill(1, 1, 50, 5, "")
$icGPU.fill(1, 2, 50, 1, " ")
$icGPU.set(2, 2, "KittenOS NEO Installer")
end
function $icOctalToNumber($a0)
if $a0 == "" then return 0 end
return $icOctalToNumber($a0:sub(1, -2)) * 8 + ($a0:byte(#$a0) - 48)
end
$icSectorsRead = 0
$iBlockingLen = 512
function $iBlockingHook($a0)
if $icBytesRemaining > 0 then
${
$L|icByteAdv = math.min(512, $icBytesRemaining)
$icBytesRemaining = $icBytesRemaining - $icByteAdv
if $icFile then
$filesystem.write($icFile, $a0:sub(1, $icByteAdv))
if $icBytesRemaining <= 0 then
$filesystem.close($icFile)
$icFile = nil
end
end
$}
else
$icFilename = $a0:sub(1, 100):gsub("\x00", "")
-- this sets up the reading/skipping of data
$icBytesRemaining = $icOctalToNumber($a0:sub(125, 135))
if $icFilename:sub(1, 2) == "./" and $icFilename ~= "./" then
$icFilename = $icFilename:sub(3)
if $icFilename:sub(#$icFilename) == "/" then
$filesystem.makeDirectory($icFilename)
else
$icFile = $filesystem.open($icFilename, "wb")
if $icBytesRemaining == 0 then
$filesystem.close($icFile)
$icFile = nil
end
end
end
end
-- UPDATE DISPLAY --
$icSectorsRead = $icSectorsRead + 1
if $icScreen and $icGPU then
$icGPU.fill(1, 2, 50, 1, " ")
$icGPU.set(2, 2, "KittenOS NEO Installer : " .. $icFilename)
$icGPU.fill(2, 4, 48, 1, "")
$icGPU.fill(2, 4, math.ceil(48 * $icSectorsRead / $$SECTORS), 1, " ")
end
if $icSectorsRead % 16 == 0 then
$computer.pullSignal(0.01)
end
if $icSectorsRead == $$SECTORS then
$filesystem.close($readInFile)
$filesystem.remove("init.neoi.lua")
$computer.shutdown(true)
end
end

View File

@ -1,71 +0,0 @@
-- KOSNEO installer base
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
$computer = computer
$component = component
assert($component, "KittenOS NEO installer: Copy as init.lua to the target disk, then remove other disks & reboot.")
$filesystem = $component.proxy($computer.getBootAddress())
$filesystem.remove("init.neoi.lua")
$filesystem.rename("init.lua", "init.neoi.lua")
$readInFile = $filesystem.open("init.neoi.lua", "rb")
$iBlockingBuffer = ""
$iBlockingLen = $$CORESIZE
${
function $iBlockingHook($L|lBlock)
-- Run the next script (replacement compression engine,)
assert(load($lBlock))()
end
$}
${
function $engineOutput($L|lBlock)
$iBlockingBuffer = $iBlockingBuffer .. $lBlock
while #$iBlockingBuffer >= $iBlockingLen do
$lBlock = $iBlockingBuffer:sub(1, $iBlockingLen)
$iBlockingBuffer = $iBlockingBuffer:sub($iBlockingLen + 1)
$iBlockingHook($lBlock)
end
end
$}
$engineInput = $engineOutput
while true do
$readInBlock = $filesystem.read($readInFile, 1024)
${
for i = 1, #$readInBlock do
-- Read-in state machine
-- IT IS VERY IMPORTANT that read-in be performed char-by-char.
-- This is because of compression chain-loading; if the switch between engines isn't "clean",
-- bad stuff happens.
-- This character becomes invalid once
-- it gets passed to engineInput,
-- but that's the last step, so it's ok!
$L|readInChar = $readInBlock:sub(i, i)
if not $readInState then
if $readInChar == "\x00" then
$readInState = 0
end
elseif $readInState == 0 then
if $readInChar == "\xFE" then
$readInState = 1
else
$engineInput($readInChar)
end
else
$engineInput($readInChar)
$readInState = 0
end
end
$}
end

View File

@ -1,34 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
return {
read = function (fn)
local f = io.open(fn, "rb")
local d = f:read("*a")
f:close()
return d
end,
write = function (fn, data)
local f = io.open(fn, "wb")
f:write(data)
f:close()
end,
progress = function ()
io.stderr:write("00% \\")
local lastPercent = 0
local chr = 0
return function (fraction)
local percent = math.ceil(fraction * 100)
if percent ~= lastPercent then
lastPercent = percent
chr = (chr + 1) % 4
io.stderr:write(string.format("\8\8\8\8\8%02i%% %s", percent, ("\\|/-"):sub(chr + 1, chr + 1)))
end
end
end
}

View File

@ -1,195 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This library helps in crunching down the installer a bit further.
local sequences = {
{"\n", " "},
{" ", " "},
{" #", "#"},
{"# ", "#"},
{" ,", ","},
{", ", ","},
{" (", "("},
{"( ", "("},
{" )", ")"},
{") ", ")"},
{" {", "{"},
{"{ ", "{"},
{" }", "}"},
{"} ", "}"},
{" <", "<"},
{"< ", "<"},
{" >", ">"},
{"> ", ">"},
{" *", "*"},
{"* ", "*"},
{" ~", "~"},
{"~ ", "~"},
{" /", "/"},
{"/ ", "/"},
{" %", "%"},
{"% ", "%"},
{" =", "="},
{"= ", "="},
{" -", "-"},
{"- ", "-"},
{" +", "+"},
{"+ ", "+"},
{".. ", ".."},
{" ..", ".."},
{"\"\" ", "\"\""},
{"=0 t", "=0t"},
{">0 t", ">0t"},
{">1 t", ">1t"},
{"=1 w", "=1w"},
{"=380 l", "=380l"},
{"=127 t", "=127t"},
{"<128 t", "<128t"},
{"=128 t", "=128t"},
{">255 t", ">255t"},
{"=512 t", "=512t"}
}
local function pass(buffer)
local ob = ""
local smode = false
while #buffer > 0 do
if not smode then
local ds = true
while ds do
ds = false
for _, v in ipairs(sequences) do
if buffer:sub(1, #(v[1])) == v[1] then
buffer = v[2] .. buffer:sub(#(v[1]) + 1)
ds = true
end
end
end
end
local ch = buffer:sub(1, 1)
buffer = buffer:sub(2)
ob = ob .. ch
if ch == "\"" then
smode = not smode
end
end
return ob
end
-- Context creation --
return function ()
local forwardSymTab = {}
local reverseSymTab = {}
local temporaryPool = {}
local stackFrames = {}
local log = {}
local possible = {}
for i = 1, 52 do
possible[i] = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"):sub(i, i)
end
local function allocate(reason)
for _, v in pairs(possible) do
if not reverseSymTab[v] then
reverseSymTab[v] = reason
return v
end
end
end
local function allocTmp(id)
assert(not forwardSymTab[id], "var already exists: " .. id)
local val = table.remove(temporaryPool, 1)
if not val then val = allocate("temporary") end
forwardSymTab[id] = val
table.insert(log, "allocTmp " .. id .. " -> " .. val)
return val
end
local lexCrunch = {}
function lexCrunch.dump(file)
file:write("forward table:\n")
for k, v in pairs(forwardSymTab) do
file:write(k .. " -> " .. v .. "\n")
end
file:write("reverse table (where differing):\n")
for k, v in pairs(reverseSymTab) do
if forwardSymTab[v] ~= k then
file:write(v .. " -> " .. k .. "\n")
end
end
file:write("log:\n")
for k, v in ipairs(log) do
file:write(v .. "\n")
end
end
function lexCrunch.process(op, defines)
-- symbol replacement
op = op:gsub("%$[%$a-z%{%}%|A-Z0-9]*", function (str)
if str:sub(2, 2) == "$" then
-- defines
assert(defines[str], "no define " .. str)
return defines[str]
end
local com = {}
for v in str:sub(2):gmatch("[^%|]*") do
table.insert(com, v)
end
if com[1] == "L" then
assert(#com == 2)
local id = "$" .. com[2]
assert(stackFrames[1], "allocation of " .. id .. " outside of stack frame")
table.insert(stackFrames[1], id)
return allocTmp(id)
elseif com[1] == "{" then
assert(#com == 1)
table.insert(stackFrames, 1, {})
return ""
elseif com[1] == "}" then
assert(#com == 1)
for _, id in ipairs(table.remove(stackFrames, 1)) do
table.insert(temporaryPool, forwardSymTab[id])
forwardSymTab[id] = nil
end
return ""
else
assert(#com == 1)
local id = "$" .. com[1]
-- normal handling
if forwardSymTab[id] then
return forwardSymTab[id]
end
local v = allocate(id)
forwardSymTab[id] = v
return v
end
end)
-- comment removal
while true do
local np = op:gsub("%-%-[^\n]*\n", " ")
np = np:gsub("%-%-[^\n]*$", "")
if np == op then
break
end
op = np
end
-- stripping
while true do
local np = pass(op)
if np == op then
return np
end
op = np
end
return op
end
return lexCrunch
end

View File

@ -1,40 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- Status Screen --
local target = ...
local u = require("libs.frw")
local instSize = #u.read(target)
local status = ""
local statusDetail = ""
local blinkI = ""
if instSize > 65536 then
blinkI = "5;31;"
status = " DO NOT SHIP "
statusDetail = "The installer is too big to ship safely.\nIt's possible it may crash on Tier 1 systems.\nUpgrade the compression system or remove existing code."
elseif instSize > 64000 then
blinkI = "33;"
status = " Shippable * "
statusDetail = "The installer is getting dangerously large.\nReserve further room for bugfixes."
else
blinkI = "32;"
status = " All Green "
statusDetail = "The installer is well within budget with room for features.\nDevelop as normal."
end
io.stderr:write("\n")
local ctS, ctM, ctE = " \x1b[1;" .. blinkI .. "7m", "\x1b[0;7m", "\x1b[0m\n"
io.stderr:write(ctS .. " " .. ctM .. " " .. ctE)
io.stderr:write(ctS .. status .. ctM .. string.format(" %07i ", 65536 - instSize) .. ctE)
io.stderr:write(ctS .. " " .. ctM .. " " .. ctE)
io.stderr:write("\n")
io.stderr:write(statusDetail .. "\n")
io.stderr:write("\n")
io.stderr:write("Size: " .. instSize .. "\n")
io.stderr:write(" max. 65536\n")
io.stderr:write("\n")

View File

@ -1,32 +0,0 @@
# The Symbol Guide
## lexCrunch commands
The following prefixes are really special,
and are lexcrunch's responsibility:
"$$THING" : These are defines.
"$Thing" : Writes a global into stream. If not already allocated, is allocated a global.
"${" : Opens a frame.
"$}" : Closes a frame. (Attached temps are released.)
"$L|THING" : Allocates THING from temp pool, attaches to stack frame, writes to stream.
Use inside a comment to erase the written symbol
## Conventions
The rest are convention:
"$iThing" symbols are Installer Wrapper.
"$icThing" symbols are Installer Core.
"$dfThing" symbols are DEFLATE Engine.
"$bdThing" symbols are BDIVIDE Engine.
"$a0", "$a1", etc. are Local Symbols.
DEPRECATED, THESE ARE AN OLD MECHANISM, USE FRAMED TEMPS INSTEAD.
These are reserved only for use in locals.
(For loops count.)
"$lThing" symbols are used to name Local Symbols using aliases.
NO THEY ARE NOW USED FOR ALL TEMPS, INCLUDING LOCAL SYMBOLS

View File

@ -1,14 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- Example compression engine.
-- Given: data, lexCrunch
-- returns compressionEngine, compressedData
return function (data, lexCrunch)
return lexCrunch.process(" $engineInput = $engineOutput ", {}), data
end

97
insthead.lua Normal file
View File

@ -0,0 +1,97 @@
-- KOSNEO inst.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
C, O, G, D = component, computer
assert(C, "To install, please copy as init.lua to a blank disk or a system to update, then remove all other disks and reboot.")
sa = C.list("screen", true)()
if sa then
G = C.list("gpu", true)()
if G then
G = C.proxy(G)
G.bind(sa)
G.setForeground(0xFFFFFF)
G.setBackground(0x000000)
G.setResolution(50, 5)
G.setDepth(1)
G.fill(1, 1, 50, 5, " ")
G.setBackground(0xFFFFFF)
G.setForeground(0x000000)
G.fill(1, 2, 50, 1, " ")
G.set(2, 2, "KittenOS NEO Installer")
end
end
D = C.proxy(O.getBootAddress())
tFN,tFSR,tW,tF="Starting...",0,0
function tO(oct)
local v = oct:byte(#oct) - 0x30
if #oct > 1 then
return (tO(oct:sub(1, #oct - 1)) * 8) + v
end
return v
end
function tA(s)
if tW > 0 then
tW = tW - 1
return
end
if tF then
local ta = math.min(512, tFSR)
D.write(tF, s:sub(1, ta))
tFSR = tFSR - ta
if tFSR == 0 then
D.close(tF)
tF = nil
end
else
tFN = s:sub(1, 100):gsub("\x00", "")
local sz = tO(s:sub(125, 135))
if tFN:sub(1, 5) ~= "code/" then
tW = math.ceil(sz / 512)
else
tFN = tFN:sub(6)
if tFN == "" then
return
end
if tFN:sub(#tFN) == "/" then
tW = math.ceil(sz / 512)
D.makeDirectory(tFN)
else
tF = D.open(tFN, "wb")
tFSR = sz
if tFSR == 0 then
D.close(tF)
tF = nil
end
end
end
end
end
sN, sC = 0, 0
function sector(n)
tA(n)
sN = sN + 1
if G then
local a = sN / sC
G.fill(1, 2, 50, 1, " ")
G.set(2, 2, "KittenOS NEO Installer : " .. tFN)
G.setForeground(0xFFFFFF)
G.setBackground(0x000000)
G.fill(2, 4, 48, 1, " ")
G.setBackground(0xFFFFFF)
G.setForeground(0x000000)
G.fill(2, 4, math.ceil(48 * a), 1, " ")
end
if sN % 8 == 0 then
O.pullSignal(0.05)
end
if sN == sC then
dieCB()
O.shutdown(true)
end
end

View File

@ -1,17 +0,0 @@
{
["components"]={
{"screen","67c66973-51ff-4aec-29cd-baabf2fbe346",},
{"gpu","9b7fe47d-77d0-1623-0dc5-126752e3f2c7",44800,},
{"eeprom","94a9cc26-347e-cc46-7faf-0c131bb7d8b7",4096,256,"EEPROM",},
{"computer","36bc34ad-8c4a-d19a-10e3-0162c6f3295a",1.04858e+06,},
{"filesystem","tmpfs",true,"tmpfs",},
{"filesystem","c1-sda",},
{"filesystem","c1-sdb",},
{"keyboard","dd142450-613c-435d-6049-743b27434cc1","67c66973-51ff-4aec-29cd-baabf2fbe346",},
{"modem","460ae893-9aa9-f10d-6bb0-462199da7376",56000,8192,8,},
{"internet","ef97c750-d30a-ad33-5321-6e7a64ba3baa",true,true,},
{"data","c5243e5f-cd2f-6c39-dfb2-5a788dcdef7c",["tier"]=1,},
{"sandbox","ece7b9ba-1625-f3f6-d74d-6e641a5de07f",},
},
["system"]={["allowBytecode"]=false,["allowGC"]=false,["maxTcpConnections"]=4,["timeout"]=math.huge,},
}

View File

@ -1,10 +0,0 @@
#!/bin/sh
# Copyright (C) 2018-2021 by KittenOS NEO contributors
#
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
# THIS SOFTWARE.
ocvm .

140
laboratory/ocemu.cfg Normal file
View File

@ -0,0 +1,140 @@
--OCEmu configuration. Designed to mimic HOCON syntax, but is not exactly HOCON
--syntax.
ocemu {
--Client side settings, presentation and performance related stuff.
client {
--The sample rate used for generating beeps of computers' internal speakers.
--Use custom values at your own responsibility here; if it breaks OC you'll
--get no support. Some potentially reasonable lower values are 16000 or even
--8000 (which was the old default, but leads to artifacting on certain
--frequencies).
beepSampleRate=44100
--The base volume of beeps generated by computers. This may be in a range of
--[0, 127], where 0 means mute (the sound will not even be generated), and
--127 means maximum amplitude / volume.
beepVolume=32
--The color of monochrome text (i.e. displayed when in 1-bit color depth,
--e.g. tier one screens / GPUs, or higher tier set to 1-bit color depth).
--Defaults to white, feel free to make it some other color, tho!
monochromeColor="0xFFFFFF"
}
--Computer related settings, concerns server performance and security.
computer {
--The maximum size of the byte array that can be stored on EEPROMs as
--configuration data.
eepromDataSize=256
--The maximum size of the byte array that can be stored on EEPROMs as
--executable data..
eepromSize=4096
--Settings specific to the Lua architecture.
lua {
--Whether to allow loading precompiled bytecode via Lua's `load` function,
--or related functions (`loadfile`, `dofile`). Enable this only if you
--absolutely trust all users on your server and all Lua code you run. This
--can be a MASSIVE SECURITY RISK, since precompiled code can easily be
--used for exploits, running arbitrary code on the real server! I cannot
--stress this enough: only enable this is you know what you're doing.
allowBytecode=false
--Whether to allow user defined __gc callbacks, i.e. __gc callbacks
--defined *inside* the sandbox. Since garbage collection callbacks are not
--sandboxed (hooks are disabled while they run), this is not recommended.
allowGC=false
}
--The time in seconds a program may run without yielding before it is
--forcibly aborted. This is used to avoid stupidly written or malicious
--programs blocking other computers by locking down the executor threads.
--Note that changing this won't have any effect on computers that are
--already running - they'll have to be rebooted for this to take effect.
timeout=5
}
--Emulator related settings. Components, accuracy, and debugging.
emulator {
--Default components available to the computer.
components {
{"gpu", "c1-gpu-tier3", 0, 160, 50, 3},
{"gpu", "c1-gpu-tier1", 0, 50, 16, 1},
{"screen_sdl2", "c1-screen-tier3", -1, 160, 50, 3},
{"screen_sdl2", "c1-screen-tier1", -1, 50, 16, 1},
{"modem", "c1-modem", 1, false},
{"eeprom", "c1-eeprom", 9, "lua/bios.lua"},
{"filesystem", "c1-tmpfs", -1, "tmpfs", "tmpfs", false, 5},
{"filesystem", "c1-sda", 5, nil, "Workbench", false, 4},
{"filesystem", "c1-sdb", 5, nil, "Repository", false, 4},
{"filesystem", "openos", 0, "loot/openos", "openos", true, 1},
{"internet", "c1-internet", 2},
{"computer", "c1-computer", -1},
{"ocemu", "c1-ocemu", -1},
{"keyboard_sdl2", "c1-keyboard", -1}
}
--Whether to enable the emulator's extremely verbose logging.
debug=false
--Whether to choose performance over timing-accuracy.
fast=false
--Whether to return vague error messages like OpenComputers.
vague=false
}
filesystem {
--The maximum block size that can be read in one 'read' call on a file
--system. This is used to limit the amount of memory a call from a user
--program can cause to be allocated on the host side: when 'read' is, called
--a byte array with the specified size has to be allocated. So if this
--weren't limited, a Lua program could trigger massive memory allocations
--regardless of the amount of RAM installed in the computer it runs on. As a
--side effect this pretty much determines the read performance of file
--systems.
maxReadBuffer=2048
}
internet {
--Whether to allow HTTP requests via internet cards. When enabled, the
--`request` method on internet card components becomes available.
enableHttp=true
--Whether to allow TCP connections via internet cards. When enabled, the
--`connect` method on internet card components becomes available.
enableTcp=true
}
--Other settings that you might find useful to tweak.
misc {
--The maximum size of network packets to allow sending via network cards.
--This has *nothing to do* with real network traffic, it's just a limit for
--the network cards, mostly to reduce the chance of computer with a lot of
--RAM killing those with less by sending huge packets. This does not apply
--to HTTP traffic.
maxNetworkPacketSize=8192
--The maximum distance a wireless message can be sent. In other words, this
--is the maximum signal strength a wireless network card supports. This is
--used to limit the search range in which to check for modems, which may or
--may not lead to performance issues for ridiculous ranges - like, you know,
--more than the loaded area. See also: `wirelessCostPerRange`.
maxWirelessRange=400
}
--The configuration version this config was generated at. This is used to
--allow the emulator to reset/migrate parts of the config when their meaning
--has changed across versions.
version=3
}

View File

@ -4,7 +4,7 @@ ocemu {
{"gpu", "c1-gpu-tier3", 0, 160, 50, 3},
{"gpu", "c1-gpu-tier1", 0, 50, 16, 1},
{"screen_sdl2", "c1-screen-tier3", -1, 160, 50, 3},
-- {"screen_sdl2", "c1-screen-tier1", -1, 50, 16, 1},
{"screen_sdl2", "c1-screen-tier1", -1, 50, 16, 1},
{"modem", "c1-modem", 1, false},
{"eeprom", "c1-eeprom", 9, "lua/bios.lua"},
{"filesystem", "c1-tmpfs", -1, "tmpfs", "tmpfs", false, 5},

View File

@ -1,11 +0,0 @@
#!/bin/sh
# This is released into the public domain.
# No warranty is provided, implied or otherwise.
cp ocemu.cfg.default ocemu.cfg && rm -rf c1-sda c1-sdb tmpfs
mkdir c1-sda c1-sdb
echo -n c1-sda > c1-eeprom/data.bin
cd ..
./package.sh $*
cp inst.lua laboratory/c1-sda/init.lua

View File

@ -3,8 +3,11 @@
# This is released into the public domain.
# No warranty is provided, implied or otherwise.
cp ocemu.cfg.default ocemu.cfg && rm -rf c1-sda c1-sdb
cp ocemu.cfg.default ocemu.cfg && rm -rf c1-sda c1-sdb tmpfs
mkdir c1-sda c1-sdb
echo -n c1-sda > c1-eeprom/data.bin
./update.sh
cd ..
cp -r code/* laboratory/c1-sdb/
cp -r repository/* laboratory/c1-sdb/
lua clawmerge.lua repository/data/app-claw/local.lua code/data/app-claw/local.lua > laboratory/c1-sdb/data/app-claw/local.lua
cp -r laboratory/c1-sdb/* laboratory/c1-sda/

View File

@ -1,11 +0,0 @@
#!/bin/sh
# This is released into the public domain.
# No warranty is provided, implied or otherwise.
cd ..
cp -r code/* laboratory/c1-sdb/
cp -r repository/* laboratory/c1-sdb/
lua claw/clawconv.lua laboratory/c1-sdb/data/app-claw/ < claw/code-claw.lua > /dev/null
lua claw/clawconv.lua laboratory/c1-sdb/data/app-claw/ < claw/repo-claw.lua >> /dev/null
cp -r laboratory/c1-sdb/* laboratory/c1-sda/

28
mkucinst.lua Normal file
View File

@ -0,0 +1,28 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
os.execute("tar -cf code.tar code")
os.execute("cat insthead.lua > inst.lua")
local f = io.open("inst.lua", "ab")
local df = io.open("code.tar", "rb")
local sc = 0
while true do
local d = df:read(512)
if not d then break end
sc = sc + 1
end
df:close()
local df = io.open("code.tar", "rb")
f:write("dieCB = function () end")
f:write("sC = " .. sc .. "\n")
while true do
local d = df:read(512)
if d then
f:write(string.format("sector(%q)", d))
else
break
end
end
df:close()
f:close()

11
package-gold.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
# This is released into the public domain.
# No warranty is provided, implied or otherwise.
stat repobuild/data/app-claw/local.lua && rm -rf repobuild
mkdir repobuild
cp -r code/* repobuild/
cp -r repository/* repobuild/
cp inst-gold.lua repobuild/inst.lua
lua clawmerge.lua repository/data/app-claw/local.lua code/data/app-claw/local.lua > repobuild/data/app-claw/local.lua

View File

@ -1,11 +0,0 @@
#!/bin/sh
# Copyright (C) 2018-2021 by KittenOS NEO contributors
#
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
# THIS SOFTWARE.
./package.sh bdvlite deflate

View File

@ -1,21 +0,0 @@
#!/bin/sh
# Copyright (C) 2018-2021 by KittenOS NEO contributors
#
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
# THIS SOFTWARE.
# Package repository using supplied inst.lua (use inst-gold.lua for repository branch)
# this is a guard check to avoid removing repobuild if it's blatantly
# not the actual repobuild directory (this is an rm -rf after all)
stat repobuild/data/app-claw 1>/dev/null 2>/dev/null && rm -rf repobuild
mkdir -p repobuild
cp -r code/* repobuild/
cp -r repository/* repobuild/
cp $1 repobuild/inst.lua
lua claw/clawconv.lua repobuild/data/app-claw/ < claw/code-claw.lua > repobuild/data/app-claw/local.c2l
lua claw/clawconv.lua repobuild/data/app-claw/ < claw/repo-claw.lua >> repobuild/data/app-claw/local.c2l

View File

@ -1,27 +1,20 @@
#!/bin/sh
# Copyright (C) 2018-2021 by KittenOS NEO contributors
#
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
# THIS SOFTWARE.
# This is released into the public domain.
# No warranty is provided, implied or otherwise.
rm code/data/app-claw/*
mkdir -p code/data/app-claw
lua claw/clawconv.lua code/data/app-claw/ < claw/code-claw.lua > /dev/null
rm code.tar
# Hey, look behind you, there's nothing to see here.
# ... ok, are they seriously all named "Mann"?
cd code
tar --mtime=0 --owner=gray:0 --group=mann:0 -cf ../code.tar .
cd ..
tar --owner=gray:0 --group=mann:0 -cf code.tar code
lua heroes.lua `wc -c code.tar` | lua bonecrunch.lua > inst.lua
echo -n "--[[" >> inst.lua
cat com2/code.tar.bd >> inst.lua
echo -n "]]" >> inst.lua
# The Installer Creator
cd inst
lua build.lua `git status --porcelain=2 --branch | grep branch.oid | grep -E -o "[0-9a-f]*$" -` ../code.tar $* > ../inst.lua
lua status.lua ../inst.lua
cd ..
# Common Repository Setup Code
./package-repo.sh inst.lua
stat repobuild/data/app-claw/local.lua && rm -rf repobuild
mkdir repobuild
cp -r code/* repobuild/
cp -r repository/* repobuild/
cp inst.lua repobuild/
lua clawmerge.lua repository/data/app-claw/local.lua code/data/app-claw/local.lua > repobuild/data/app-claw/local.lua

View File

@ -0,0 +1,609 @@
-- KOSNEO inst.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- PADPADPADPADPADPADPADPAD
local C, O, G, D = component, computer
local sAddr = C.list("screen", true)()
if sAddr then
G = C.list("gpu", true)()
if G then
G = C.proxy(G)
G.bind(sAddr)
G.setForeground(0xFFFFFF)
G.setBackground(0x000000)
G.setResolution(50, 5)
G.setDepth(1)
G.fill(1, 1, 50, 5, " ")
G.setBackground(0xFFFFFF)
G.setForeground(0x000000)
G.fill(1, 2, 50, 1, " ")
G.set(2, 2, "KittenOS NEO Installer")
end
end
D = C.proxy(O.getBootAddress())
local file = nil
local fileName = "Starting..."
local fileSizeRm = 0
local ws = 0
local convoct
convoct = function (oct)
local v = oct:byte(#oct) - 0x30
if #oct > 1 then
return (convoct(oct:sub(1, #oct - 1)) * 8) + v
end
return v
end
local function sectorCore(sector)
if ws > 0 then
ws = ws - 1
return
end
if file then
local takeaway = math.min(512, fileSizeRm)
D.write(file, sector:sub(1, takeaway))
fileSizeRm = fileSizeRm - takeaway
if fileSizeRm == 0 then
D.close(file)
file = nil
end
else
local name = sector:sub(1, 100):gsub("\x00", "")
local sz = convoct(sector:sub(125, 135))
if name:sub(1, 5) ~= "code/" then
ws = math.ceil(sz / 512)
else
if name:sub(#name, #name) == "/" then
ws = math.ceil(sz / 512)
D.makeDirectory(name:sub(6))
else
fileName = name:sub(6)
file = D.open(fileName, "wb")
fileSizeRm = sz
if file then
if fileSizeRm == 0 then
D.close(file)
file = nil
end
end
end
end
end
end
local dieCB = function () end
local sectorNum = 0
local sectorCount = 0
local function sector(n)
sectorCore(n)
sectorNum = sectorNum + 1
if G then
local a = sectorNum / sectorCount
G.fill(1, 2, 50, 1, " ")
G.set(2, 2, "KittenOS NEO Installer : " .. fileName)
G.setForeground(0xFFFFFF)
G.setBackground(0x000000)
G.fill(2, 4, 48, 1, " ")
G.setBackground(0xFFFFFF)
G.setForeground(0x000000)
G.fill(2, 4, math.ceil(48 * a), 1, " ")
end
if sectorNum % 8 == 0 then
O.pullSignal(0.05)
end
if sectorNum == sectorCount then
dieCB()
O.shutdown(true)
end
end
sectorCount = 260
D.remove("init-symsear.lua")
D.rename("init.lua", "init-symsear.lua")
local instHandle = D.open("init-symsear.lua", "rb")
dieCB = function ()
D.close(instHandle)
D.remove("init-symsear.lua")
end
local syms = {" ","s","e","t","a","i","\24","(","r",".","\
","p","\"","o","c","m",", ","\1","l","n",")","d","u","\17","\9","x","-","\7","\6","\8","\4","\13","\2","\3","\5","g","\14","\21","\11"," then\
","1","w"," ","\12","\18","\22","f","F","y",",","\20","re","b","k"," = ","\23","return "," local ","\16","v"," if ","\
","\15","\19","\
"," ","[","en","/","0","]","path","nction (","\
","se","h"," =","or","S","T","le","\
local ",")\
","= fu","on"," == ","ne",")\
","functio","\
end\
","D","{"," t","n ","oc","lo"," end\
","\0\0\0\0\0\0\0\0\0","un"," i","W","\0\0\0\0\0\0","fu","et"," end\
","then ","nd","ni","A","ing"," tab","loca","etting",")\
","ct","C","P","}","\
end\
",")\
","onit","= ","end\
","\0","I","Y"," do\
","return\
","le.inser"," = func","= \"","al"," r"," e","in","he","nc"," e","j","donkonit","tion ()",") do\
"," the","\
",")\
","ur","#","+","N","cursor",".inse",", v ","nction (w"," neo.req"," for "," then\
","\0\0\0\0\0\0\0","nd\
","()\
","neo.","ath","table","l ","do","2","3",":","H"," = nil\
","nd\
e","hen\
","\7local ","indowCore",")\
end\
","d\") end\
"," = false","if ","pairs(","dow","string\"","ti","O","uestAcces","nd\
if ","icode."," if","v in ","pkg, pid","000","750\0000001","end\
e","750\0000000"," else\
","window","end","00",".neo.sys.","neoux.tc","= {}\
","(window, ","5","=","E","R",") end\
"," cursor","request","ode/app"," return e","\" the","equire("," "," then",".neo.pub.","hile true"," end"," en","\0\0","B","M"," ret","for ","in ipai"," true\
","close","code.sub","error(\"","return t","oroutine"," end\
if","end\
en","tion ","\", funct",":sub(","vailable"," end\
"," l"," == \"","prima"," if type(","ryWindow","window.s"," end\
","L","X","~","on ("," in ipair"," for _, v"," for k","\0\0\0\0\0cod","lose()\
"," = funct","rror(\"Exp","nsurePath","s.primary","primary","buildingS","unic"," "," re","surfaces","disallowe","ackground","neoux.","ccess","selectUnk"," end\
"," = uni","\
end\
"," f","7","Z","z","for k","rue\
end\
","ode.len("," end\
","lse\
",") end","end\
"," fu",":sub(1, ","sub(1, 4)"," ","neo","the","if not "," th","tion","unknownA","end\
","[1] == \"","= unico","Acces","\
end\
\
l","n\
","\") end\
","de.len("," end","\" then\
","0\0000001","urface","nd\
","()\
","(surfaces"," if ","\0\0\0\0\0\0\0\0","unc","urn","\
"," end\
"," neoux","ction","\
end\
","\
","neoux","\24\0\0\0\0\0\0\0\0","\
end"," end\
end\
","\
end\
"," if "," err","s[1] == \"","unicode."," window"," if ","ocal fun","= false","\0\0\0\0\0\0\0000","\0\0\0\0\0\0\00000","code","_, v in i","()\
","d\
","f ev == \""," return",", bg, fg"," end"," neoux.","\0\0\0","w.close(","\
if ev"," i"," if not ","if kc == ",")\
end\
","indow","onkonit","00175","table.",", bg, fg)"," l"," end\
",") == \"","d\
"," return ","rn","ca","q"," tabl"," error(\"","end\
re"," in ","hen err","nd\
en","nd\
","\
end","turn ","x.neo.","ion ("," table","false","string","e.len(","d\
"," re","1, unicod","cursorX ","local ","end\
end\
","cal","\" then e","nd\
if k","ion","00644\00000"," end\
","\
end"," window."," e","if not","== \"",", func"," neo.",", functi","\
end\
end"," r","end\
","ion (w)\
","\
end"," = false\
"," ret","tu"," end\
","f ","unction","= nil\
","th","n\
","6","U","_","requir","eturn\
"," funct","eturn tr","\
\
local"," table.","eques","rn ",")\
r",".insert(","ode.",")\
end","== ","\0\0\0\0\0\0\0c","n\
","\0\0\0\0\0","()\
"," = nil","able.","n\
"," = n","return","000644\0000","Access","able","etu","d\
","\
if ","end\
end","nction (p","ub(1, 4) "," return "," return","lse\
","abl","= uni","for k, ","\0\0\0\0\0\0\0\0c","cursorY","ble","bg, fg)\
","\
e","ind","ret","tring\"","000000"," neo","pairs","then\
",")\
","le.inse","loc","\
end","n error","\
end\
\
","if s[1] =","lse\
","turn","ursor","function","neou","\0\0\0\0\0\0c","function ","\0000001","end\
e","eo.","Access(\""," re"," lo","\
e",")\
end","urn ","\
re"," end\
e"," = f","\0\0\0\0","cal ","\0000000644\0","in ip","\
en"," return",")\
i","ction ","\
end\
","ode","equire(\"","r(\"Expect","ctio"," cursorY","if","%","4","<","G","\
local f","\
ret"," for ","d\
if"," erro","true\
end","ed\") end","hen ","nd\
en","al ","on ","] then\
","string\")","for _, ","unicode.l","[1] == ","true\
","uestAc","nd\
end\
"," for k, "," if ev ","then\
","\
en",")\
en","do\
","e.ins",".primary"," true","coroutine","return\
","airs(",") end\
","nd\
end\
\
","\
return","window, ","end\
e","end\
end","750\0000","eturn t",", functio"," e","d\") end","en\
","ocal","icode.len","e\
","n\
","w.close()"," uni","sub(","n ipairs("," for ","end\
","ion ()","end\
en","quest","cursorY ","eturn","unicode.s","x.neo.pub"," = neo","hen\
","then\
","string\") ","in ipa"," retur","indowCor"," re","750\00000"," do\
",")\
if "," then ret","\") end"," en","n\
","ipairs"," then err","d\
en","= f"," local",".neo.","\24\0\0\0\0\0\0\0","func","()\
",")\
en","curs","\
loca","ode/apps","nd\
if "," i"," = neo.r","kg, pid, ","\
i","win","code.sub(","require","wind"," else\
","close()"," end\
e","\
\
local "," then re","\
e",".request","wnAvailab","tion (","\
local ","error(","\0000001750\0","n\
","d\
"," ","uestA","reques","end\
end\
\
","ion ()\
"," curso","then\
","0001750","surfac",".close("," function","= neo.","if type("," loc","d\
e","oroutine."," do\
"," else\
","tion ()\
",", v in ","window.","neo.re","0000","\000000","\
end\
","ipairs("," ret","\"x.neo.pu","error","\
i","\
end","()\
","= nil\
","nsert(","n erro","rror(\"","nil\
","cursorX ="," do\
"," for","turn f","en\
"," table.","\
","\") end\
","\") end\
",".close()\
","\
re","d\
e","\
end\
","ownAvaila","000644\00000"," false\
","\
re","false\
","ion ()\
",")\
e","urn end\
",")\
if","x.neo.sys","indow, ","then\
",")\
if t","cti","ion ","ion (w","\" then"," = nil\
","then","e\" then\
"," if","on (w)\
",") do\
"," en","tAccess(\"","surePath"," end\
end","\
end\
","e.inse"," table.i"," local","oca","n (","0 then\
",") end\
"," loc","se\
","do\
","nd\
end\
"," then e","0000644\0","ion ()\
","ocal ","end\
if ","e\
"," then\
"," local","icode.le","\" then "," ==","] == \"","eoux","ndow","\0\0\0\0\0c","nownAvail"," functi","0\000000000","eoux.tc","hen error"," = neo.","table.ins"," window","(window","d\
if ","\
if ","\
local","end\
e","nd\
end","insert("," local ","k, v i","surface","eturn ","\
loc","sub(1, ","end\
if ","io","!","$","&","'","*","8","9",";",">","?","@","J","K","Q","V","\\","^","|","<22>","…","ˆ","‰","Œ","<22>","Ž","<22>","<22>","","“","”","˜","¦","¬","¼","½","Â","ï",}
local bytBuf = ""
local bitBuf = ""
local function getByte()
if bytBuf == "" then
bytBuf = D.read(instHandle, 64)
end
local r = bytBuf:byte()
bytBuf = bytBuf:sub(2)
return r
end
while true do
if getByte() == 0 then break end
end
local function pb(c, p) if c % (p * 2) ~= c % p then bitBuf = bitBuf .. "1" else bitBuf = bitBuf .. "0" end end
local stn = 0
local function getBit()
if bitBuf == "" then
local c = getByte()
c = (c - stn) % 256 stn = stn + 3
pb(c, 1)
pb(c, 2)
pb(c, 4)
pb(c, 8)
pb(c, 16)
pb(c, 32)
pb(c, 64)
pb(c, 128)
end
local bit = bitBuf:sub(1, 1) == "1"
bitBuf = bitBuf:sub(2)
return bit
end
local buf = ""
local mode = false
local bc2 = 10
while true do
local bc = getBit()
local v = 0
if bc then bc = bc2 v = 64 else bc = 6 end
for bit = 0, bc - 1 do
if getBit() then v = v + (2 ^ bit) end
end
buf = buf .. syms[v]
if mode then
while #buf >= 512 do
sector(buf:sub(1, 512))
buf = buf:sub(513)
end
else
if #buf == 27939 then
stn = 0
bc2 = 11
bitBuf = ""
syms = {}
while #buf > 0 do
local len = buf:byte()
if len > 127 then error("symlen") end
buf = buf:sub(2)
local ch = buf:sub(1, len)
buf = buf:sub(len + 1)
table.insert(syms, ch)
end
mode = true
end
end
end
--[[°%ª<>°œ]-¤åEØr=´ÚZE‡N×ϯp¢œŸìᄉ±ä<C2B1>ôžüƈ¤ØÓ¦°4Þ.6ÆÊy3ƒ·ÿ…ÍbHÊM¼ÑväàäàÂìk
 £h 3/U<EFBFBD>š-<EFBFBD>3PMn4´©;<EFBFBD>FÅag·Ž¸µæ­žãâÙ<EFBFBD>¹áÀxÅ-Õ {.wÄëÒ1íÃñ`&RèßöO÷?!øè1-y ¾Oµl!´m§<ƒ;©.­ýç@¾hPÈÝb!dÙd´®<EFBFBD>{¹Öÿ¨8<EFBFBD>©@óªëÀ1F
ØBÃS·y­Þ]ÎãS
nn9ÂZKá_²6"‡ ¤qd2YZúkÌ(ðÈûb f_“±<E2809C>èˆuî¾â}šžÍ„°¤1õ¹Ø Sôl¹¶ùÌüdÎx¬3ø§àOë¾
{=»íæ|"£‰Ÿ9s'`<60>%¯@*±QR?Š\>eÎ yv>ÝÃ2µ“î9·¡¯ˆ…(
<EFBFBD>/¨²5ÀösÐŒ<EFBFBD>Çáænîs'¯“êcÚûFnňðÝr‰O/DIj,yéBÕ¿ë“— AUÐ<55>^\\ÇŽ¢è7“0xý1”"yŒ¶ ¾ªÐg¶×àÃ+/€I`Þ™.:ØGóÒÚm!óu¾ÕéöeG4 ¦¬Tž“.£ÿ½µŠp‡X*¿Y(1Úk˜¬°Þyñ ‰Àaox
rÏéÔ¤-*λ*©¸×B?£ÒAÏiRõi6á;[«à,òH#åU(;]ôFS¥óÇv])é]§Ö<EFBFBD>pgîårê/ˆi4(¢W+³!O+Të.R9ýºbåÞ`úúö?éäÆ<EFBFBD>Ê<EFBFBD>×⊠0E"e/\•&žP5¥<35>0ŒS¹ºUÊká6òIm0Œ¨æÕq„-kEÜ·7~Äò§ò¾‹ÓÙ£é[<5B>€¼Ò¬7K¶Å~îqÈäÚ=à-fÚ€?‡ñ8 üûÜmf*<2A>òZ<5A>Êq3¹MIS»MSÍn£S˜”¬¢<C2AC>jw¨pñr˜(Àº"ª©ó¦+Ü÷n­¼iU¢æ¸]UMìµNÖìÛ<EFBFBD>öâ¾[_n#ÉÜ-<¨ãoƨ:)æ}3lhUYúìèá¤.^챟è×~Õ ?hÔî·ò®"bYÌ+Ò•Þš2¹<32> ] Îãéó§Ü|ê|ê¼` ]©]#ÌMœ«[nJ“bâ6º@éMyHš<48>¬¸ÃÎi<>jkZh:o`ÇIñ“Y=“¥²0J
XdP±ô±
3óÍF, ~É óÿß;BTI((¤ªh9*·j4ÉeYè¯ÐØ\ã¯íÅ¥&Tðo=÷Ž;Öðÿþh²(+-ò<ƒÚÚ7¸ºy¿ÅV´öó&Xl6ÿäüâ³:úˆ )+L¿07PRCõORYÂ.EíWx0?_tKË$ý>š7ïÿ8 |<EFBFBD>bB¿íÐÖ,£.Ýu{±AÍå¾ILNääÂáx¸w²u1´óø=B Z!Ïaò+}/c´ˆ,Emqïs¿o]cBÔ Tdwñ¡°'Îâ­4ðë¶[ÆOæÀI¬%œÿúæåüµmL<12>ƒ…fÄ]°Õ>«t¡ÛKCJ8†`~rUv†QÚÔ]îYÙ¬‡‰7˜–šó“»Ê6¢¤ ò5*´žëœ“{ò´ŠÏ<Ò¢0ö ëâùüÓõ
Áeí Ý¡Â!å À$sq;<EFBFBD>tÍÛkÈŒXôÎk%aæéË<EFBFBD>ŠQv·ÙAÃIÿ/ Ò&Ú?´'?qט>~%ÕÕC®T_W*%Œ\&÷m„[ñè/lÔ†sÂo"-¨ý#YÑ>F„F] <>@)<29>xA ¨êÑÅØ<C385>š5„ŽTÚÕz<C395>Ò/hlÏ¿9»îËÓ"K°±™ÌLúüª ß;ïþÙ$hoÍjù?î#v\±=‡0ß<“<äŒIbQlØešéG:‰ñ-úç­¢†GËzR;2i ¡ÃOÂo²s:-@F‡;gÔ¡zðxXï&?ÚìV]>0ec+?nVÞƒ¶I¯l7Þn©Äøq<C3B8>hé™m…ë6•g÷?×»Ÿ*Ÿ1•»|†ùÐÛ+mkFäM
Iéô^!z¡ê:öñj{3×åûæѱpƺ¼:/\6zT?ÂÓœ//eÕèLC¥gá¡ Ô¥'Åú¡ÐÓ™2 æíÞW×ïÌõ#{ü5Æ—æew\(ãêmë_%×ôI!1bù0Ñû<C391>‡4y9"Íæ2®±¹6°<ã"<22>V­Þŵb}°pñV"˜qžÉê-±S ¥·˜.<2E>ß®:Þø†ÙcÛˆÁL¢<ÓÉå=~ü'À8R<EFBFBD>«;˜TÐZ£ÇJNßo ù%ÿUGÅÇ ÚèG[ë#¥rKyˆ¹Èõ<EFBFBD>fÉîë¦×¬ °ºìª àË¢Ä`îÚƒ`³ØÕà)ßÐRÐÅþî<EFBFBD>FXCÍMû¨È¢6<EFBFBD>í@CWô<EFBFBD>ŽqC,å#ÁhiÑŽÎLš÷K¦YÓE¤@K×nZ˜¾h£)9°1Òµõú(dz·Ê¸<EFBFBD><EFBFBD>:füUƒ&9Ÿ£ <EFBFBD>r*ÿ2b°¥g>Í~<EFBFBD>äËMtaéžôºÉ è)\K²y@é9`ŽE*­ý0©5³&<EFBFBD>­þ´Jäÿjî2ÖÞÝp
#èi<EFBFBD>M-i8
èÀ2Õ)%ÓÄœ2íï¡m·BM âJò^šîàÍm~Ü@~Œ ó$šˆŸ¤4°ÓÖ¥=$ Éb»ç<EFBFBD>ð2¨ò ¨ §!n Îáxê!Ð>2øàÁˆÁ©1aÑ_=[â易GÜõÝ<0ˆŽ<EFBFBD>Z²?Íï»~V<EFBFBD>>^ ŸOFòLæ |ç<EFBFBD>¥>ÌÉý<EFBFBD>ˆÊBò¿[5T^Š=G\ÅåMUMzš«m¹¦||ŸÃÖÃ΃¹Ê©˜íügÊx³`ÕµlÔëN°c,{80øÑí':9üõú ˜<C399>N‰kà Þ¸³£é€Ñ8ønYP]í\é`E¢jCu<43>s|“…°«ýº£ 1<>G)x*¨wñˆ¼àýA=pÐøÌÛX7`>ß°ÿ )¬>p°}gUËDE2¸$âk߬ï!âMD
ú¬pñV·¼ì!~©%´á÷6¹¿ë(­˜´Þ<EFBFBD>ç(ŽÆðºÍØ>£³M¼ãsá
í*i ú%x?c_¼C Q¼2ðXèÂw¬ôK·´Ú=É}k/1mƒÝaRçoÉD·á³3
»ŽÉ˜4ÜÙ¢$ñ¥E#IJ2¡Ÿ«àú5éê»rÿW~4,;·&2ÍZ(]VpGÃZî
Ûç<EFBFBD>s<EFBFBD>ê¿r¡ùË'·„y€E ö ¦˜vLt¢Âl§-=´5öôÑ…úòfÔqïÜj؈¬õsu0ÿ(Z(;-a‰¯rYC¬ér÷ײÀDN±dcâ×^|Lä<š<06>…u_yðxãn[KAxð€§G½°·ú9Õ Ç¡gŸÝ\Ö¸vëfšk¨Ì¼r[ .ÃchJ`|ò( ÄÒmÅg¸*œ“tI?}CM:Æ$t¬ïzIÏË]{vZ\¶©žˆ¯<>`7œ°¤-*λÌaµÝ¯â?Ù¥ISKåì9!éðî¥ói*Z CE°•4†îaÐÂUyñ¢»lczÒ|GÐîÔQ){¶oò”7,n=‰8a<38>…4½ÂÒùæ<6A>vÐU¶€%þÒÉL שÿX·ùÄóDÆzÛK)FŒºBˆ¶i¼¿[<5B>=eÅPŽˆ=F%µŽk…BšâžÒÅ=kŸ°Ç¨:¼ƒÂüeî2ÏùÊ4X³Æ… ´T¸?9*‰Ð7ÁáΉ 9KG 0ÿ.uW¿¦Õ'`H&<Iq½µÇ(³cqWŸÛ¤°ði ëSØD4íŒí hT zœLŠh«£Š× ¹c½ÙÐ<EFBFBD>ñÁ2§£Gþf=wC ÒÆ!¯qE-A$<EFBFBD>w£§©<EFBFBD>Üç9x8aôPîc,|7qse°0¼v©8M
Žçɯ騢+³­÷Ô<EFBFBD>ÿ ÈÕÑ#šÈôçívNtÑ·ö ãä:S Te|­9Nw»NpIUl]â}è,®½Åk4vs~.«éÆ¡¹µ£çÕ{DöLÐÝ<EFBFBD>0\ÞàÕßå^ê¥![ n*×\",1ðGqpØùT™Oh$g)†±-sÂ<Áá)“Ú¸ù£I°î&ÀÝ<C380>¹U
ÐEÒùÔÑóÜîG#÷qJ>ÕµÈù¤?9 Ïž¤¾]}Å/äüq<EFBFBD>æ¡k#Ó¨rîya(Êñò|^ÂÇÉŸûð[ C°¦êŠ3-Ü¿ÉÖý×W˜:<EFBFBD>ˆä<EFBFBD>üÜEð;EV-<EFBFBD>ži&#»+?ÒH=qƒœÍ
(ϵ«»ñR|rpÔŒƒ²ã 4ž±*£×Æ6~\8¹¥¸¤âó/Z®<EFBFBD>»Ônãr]+:ãq<EFBFBD>¼³DHAy¡0çžÏfˆ(õÅÈ
kQÌš&ל <EFBFBD>¨@¿¼fàWÙ»2Úk¸e÷°-(s|ÙÝäÅc *¸>±<EFBFBD>Â`K<i<BžÎ<EFBFBD>;<EFBFBD>4óJZLi<g·ÙŠÿ¤0t[œ<EFBFBD>í )Ùÿ § Ã¥/ßõ.¿½0ÂËSÁÎX\ýìL@ ê¦ wÿ˜þ š<EFBFBD>·'"<22>4Ì5 1¹39R*Gz<47>ƒ¯ÀqFŠ¸g C@§º̵1©å_âI3£©•7'¾<EFBFBD>Á5Cnº"îQE ‰—Q°Í@! ä' œÔò<C394>÷=Ä]Ïž G8†ñèj}|a{JÆÿé ´ ÷a{°³6¸ïŸŽ ªªÃ0„_ÍÐãº6<C2BA>/Ù`º ž@óôM C6®Ü!8õœRÿø› šâ@XS[I"]N%hÉb7PÜ3ÖbçY*ei_Üf©;C{äZ蟳Ì)©[úpa¥=°7ù)F«ðJ±ç d7±>Ñžõ6PñÍ{ 'Ç›§ˆ¥ªÖhtÓxI<78>FӇˀ”aÆpí¯lg#£#‹û<05>„Ę˜8¿Ÿ=uAîXÀr/¶Ú¸ÜÞ”S³`Ù&¹éþúèÉïü{:7@vî QÐt¶!Ÿ%É(u-¿qs~M0IgsÙŸ<¿„s·òôz58ϼքå܉~TrÝÿõÁß…º$L]XGË1Ðã\Õ›!è øhpOb*]oÀ7£óÝb[%ŽMé:¯ßÒIX¾ˆÚŠZé¢û+•úãŠð+|©v,»=[F2 1lzPïÇÄ j2èº@bÝšYÓC_‡í×Åã• £Í`ß³AbÒÚÏ'ßÏL9'ƒiM\<15>n[eŠlïžQÉ—Ùüŵ÷N¡ º¯TÒº­}d!ÆÈb¡öŹ1-d°º v“öWå(».(.S:J ¡](™sbtÕ…¦ÄÉÒS^<r ]®©¹õ¿¦´‡Dum%ÍM­Ò™‰ª­-¯´:ÙÂÈ&|uã[;嵐ù³7 M¼>s<>òFŠQ#VŸÜ$æ#÷AŸ³/8d¦A˜'ŽÚË«ç³×Ë¿õ«·¨§šø°¥².»Ó³ x$Öô/'ewâ<EFBFBD>UsÌ«ø„¡>Ï;]‡LœŸ.uÅ¢éE*f.C²UDM<44>?ÆfZù^ò\\<EFBFBD>¿j†©žIÕü8€”  <>Â",S<> ­dº¹BÏÔù{Té@Ç¢æ
ŠééLí5oE:<EFBFBD>ÐÞ¾)Ñ2:+³+²§Ô½xG^½tHŒUˆœÚŽ¨£ê¯Ñ<EFBFBD>ü¨¼<EFBFBD>¥5UΞ©©lîJ <EFBFBD>Ð꿬ֽJI¢§/5ù²Ú:ÌÎ$E<EFBFBD>­\.S/L?p4u¡á>ˆéôÑƱ'ƒ+´éÅcÇ ø©‰ZO<ì±ýâ¤<C3A2>ГöV<C3B6>XçùÄ
ÒÖjlxR¦°H}ù%V-ôZt¡^æ^2o7õÇó/!Ë?à®㤩rsZnz?9 }æŽá:­ùÿçªc{$/B%?Á{Qøè;å¹ÚŠr æ ^ÿáæ:/ka­H8¦r7¬9`;¸9 KX áŒUX½bå|ö/ŽõÞÙ<EFBFBD>¾qû,I!Ü&åëa¡ö<œÇ OÝÑÿ]\ÙF®$c0:Bêó"HA&¶Ö)ø ¥æ£u)nÒ´)~<7E><>wÎvdS¬å³§ru{Ns=ñûàŸ“LßÈΣ-íN"Î<EFBFBD>ȮǢ³ê<EFBFBD>àˆÚ[ÇRÞµŽ®JmMQ°ŠÒ¬ÞÁ¨æ EC¹TDzÈ"“ÁÙÎXrAÔ_ €Ÿ¾}ø?6gŸNƒ$!•<>Þôªââ¤/H°U¿:ºî<Òïæ ]ÜÔ!{|…gÖêó'B¸
J˜¦~g#?|ÒRfäÞ-è`pðEúúÐiKd;ŸUj<EFBFBD>¸¹Zïo£Uvz(åˆ<EFBFBD><EFBFBD>W°½Ûç­(({Cÿëñ-ÙÂ+Þ× Šä{S,bë¦È
l§½ïŠSR´5Š¸ëûCEkoRc\ÚWÊàväø¡{ñÌŸÀt^ÃòI¢rÂ3ÉÍL ñáqíi£;ÿ,<EFBFBD>õÙû1ñèêžLýxk ÆÆ3Ï7nIÊ
y<EFBFBD>W#eíw/̯ó¹i f¯žÊÿ·Ê%O¸X]Ø¥ë8VPŠ`Ù!ûß×>_ûîÀþÿÿ"Ž>T<>¢+<1C>!¼=?þDJL{¦`4üîxýkß ®wò<77>ûq°é/
Ò¤Úô<¤Ãü;ÜêÝ0t«È+a/#îñ÷ñø}q{Ww-±ø×à ¢+<CE¸¸ƒ>²Óã<EFBFBD> Öpkæ]ÚÑÔœ¾<EFBFBD>³ÔíÄÎO_ B¦ {ƒvì×ÎÛý,é×é¤( þÒýdåšÈûr¿\Ÿœ¾ ìZ±30W2XÆF²1ÆzVNdq±áØèÆžÂ#*bõª®o­»:xƒhG·j ÑÎ<EFBFBD>Tu "òøTI´ùœ ÿÃaÃÁ|ìhZäK¶y4|Eá \ήzÖYl7ÓBŠ™ÍqãJC81ÓÅÔû£ùˆ }Y;ÏÍÛ$ âżøž]ì¶a;·ïW_¯÷;Zbð3ôÑ‚2ÕoóŽé{*-2h—®áI`Ã¥šçšéEkÁŸ­H5ió¿S+ŠÞÄY”½&®Á¾<C381>ìíºQ¶B-ʈ  Ò‘Üó×Ð:za÷š
ì^<|«OES <¨ 9^'^ 6ÄЊ‹Ä ®g†%X»ºË®¨ <10>7œ£|Âî+°›Ý$ øâ"…î÷þÕeûïÜK[øÌOhSY iiI« _¤šÉ¡ß<16>*šŸÈ^;,.êÝÁBW¢tÆQ$Ó‘7^ã@_Ž*}z@“…koË¢È]uáD+­õÔÁIþt4†ô´Ç_³ '<ÄÞv,zKq_Da q-äÕÈí<EFBFBD>žkèý-Õo2Ò3ƒ¸¢÷!çOƒ§¹x±Š²rMŒ+¾ÛH<EFBFBD><EFBFBD> ²®<EFBFBD>èçv²°ZàÑDí>åËù
$@"a)<29>Ö™]x"ž ´ä4²;>nSNYG»¥Þ)®î§ŽÖü¿IÕwcZt79 <EFBFBD>ßVâqë¹zdKËòêÔ R)]~äËûÝ%îrÝEÜÄÂl ¯ñæ¦æj·?xvxJLý¾´¹ª§ßÎ&Ý^±µ°@kŒs u|#⌢h¦ãC -öæâÿ|ÕñÔ w©Ø½jÞBÞ·<EFBFBD>ð_Þo
WÝãòi}c­>õ6Uc/² й;ïeM£}|H3_k|¯/grb°]Ã<EFBFBD>«)?>g<EFBFBD>ÀS"IKÀLºã€~ÆϲÐ> C8 çVÝÌ+ïÕãÛá­ìâvÕscA(Ì'….FÔ=Ïçp®|p;>ô<w> £½s—«ë€×Þ²~ja˜Ó<þzÿá…·páå%Ê5¶t­àðÛúúÿµ^£1Ó€NäLaì÷Jíp|+ø;(XAC6j“I¼ _a\í«Üú\&;×ß[ï^³vwk§x©ÛÄr±¼qÀHð‰7†‰ Ø“ø¦.¾F*éë®Üä»ÒÌ@÷e<C3B7><65>äuÝ%˜kG(%¦üŠ?o¬n1gÛ%Þ5#G(SmŠ@ÆF<êSÅ9þíic…>ƒ|gØ<67>Œ%vØúµ~Æ„žo¼þðt©Ì;Îi»DHNÎvF g¤ï´äíòqoè…㌆8“ájQÃd-¼~J<>Ce¬-É¢Ô¬ª¦TM7ééÅr>èÄ„û¬ðš.Z<>$­H*F<>
Ý cÖÅߘÏTÌý<EFBFBD>1ÒÀ+Fèn$& woô8#@<EFBFBD>NrÍ°$$9?ñ _½F
e!Ù içz}é%n>ÕV7 âÙ'åL÷Ú© Á¦â'º+.Θüî?(ò4ïßCuEüÏûi|wJoÔ=²îÚŠ©9¦}CjƒÎÇ<EFBFBD>Ï úZÈMâbHi˜>áɼƒ¸×ì<EFBFBD>Æò½z£åã³µ^Ä;¼â£¥á÷Ñ\pÛ꣤í ÿb1£þÎQ*aMü)© œÄšJæÄŠ~}´Í¦ï«Õž»êð£´ý<EFBFBD>žÁ³ÍÈB¥³·ÂªCÕqœ ^BPßhƤõÄ:qàüx`í¸Œ6Ó¥P­°;aM]V6àÅiLÓÑ¢nZÞ dtƒziäYýÉå¬'"déè÷å×°Nû.<2E>ü±…X¶N ïò)Ý h™ íjÇsBù]Ÿ#i°ÈTœSrf÷i/OR•DY„c²gmóôdƒc5ørÊ6<C38A>p<EFBFBD><EFBFBD>ÿ‰&´þö§uÑæÄAvÎá»>û(Á(fßQ÷YW<00>é‡ë2ÀuI¿œ„.AÍiàŠÊ+¤…o%ñôZê@΃³Yðrˆ1Bo~cÒsD8x³x£MEž”·îפ3ü¾ÛI¶0Bç5<ÊÚSåVÚ¤œl¼ ëu(”òaSôÅT…\\ŒÐüÿD4üY=XGšëb™×<E284A2>ø­{R D¸V0¾‡ç!“N•'½ µ>Ë!V­"YÃî@‡Hö5MïpÙî¤+÷øßð!YŸ)ƒâF/‹®êÓ#Ùs&#=¿¶}ñËFds[-~õ­æäf½†˜µ*{˜z-³ŽÎÓ—ÛùîÌ<C3AE>ͬ­Î~U,ì@âÑüFñ{E¡‡3{<0/f£a„o3JCdŽvp<76>ñ²m´ZêŽù§ <0C>5‰™E<E284A2>~™ZQöµËô~1­ç±>•mòtœ:Üi¶¯jï<6A>4n|@¾:Å·ö(S.DšL*'xsÉ-…«W…ë<E280A6>„W—$ÅVÊ­¯¥ë)­»ž?‰OÙqŠøB§¼”“" k,<EFBFBD>V¾µ
®|Ÿä¼»¥0à4ð¤QpÃÙ 'Ò$Wúª‡Ëífì;ª³P.}{¨Urß$$7°I.D:»{{»
c1*ZÖÖ<EFBFBD> }Û¨¨¡GŠAÌD<EFBFBD><EFBFBD> :ò Ÿ%ú²3Ž´u»ÁfþòîL×>y4<EFBFBD>âK 3êê¥ôtm
±e¼µ:Ï£!b<EFBFBD>.îr6¿<EFBFBD>+ev[RãÖ{ˆþ ÇñÖò8¥î<EFBFBD>ôêÏ:<EFBFBD>Òôý¦ßü:)%#«CäçןéŒìOBq$Y]|ã)ïˆý`\Å!/÷qJÃ='<#{íH×­çÇc^^|WŽÃ¢T¡zÐÿöªÃpº… UJ³ãgÃWa-GO.Aû \ì
øv£zþ!@/ø »V£Hb]×|l.®c_FIîsp³§3ìÒ<<EFBFBD>©<EFBFBD>$ãœz4pç8Î09¾ùñ«¤<EFBFBD>(2\˜$ü<nkã~.vüúJa,'è€(;‰¼
Sy}L5[fc`zoYàoOä~M6PmË)]ÎàaÕëÜù7&Âm¡FS×-}ݘ|Ròh3»œÁ# = O<EFBFBD>opÿEBö*×'mE²‰¶z·XöjÄkTËà­Šá´”³—±€üõÃÁ¦m¯y@“Ÿ_ܪ¨ò”ùk<16>èàÖÅ÷|ý ˆ&^iÔ)>Y2=Í2~.Î"ˆ5ÓL&IMMBE‰ È1‰œ­ÿvʨiòzä~ð òãªÈ Î6ÃIS¸¸C§.óµœCcÀòuhÒÒ1R»Ûõ&84úíÀ%ÿŠP<C5A0>Ð)mí^[À­]ùÌ3sRÛ>n“Q§˜:®7š»PHv~쳤r~Ž€´<E282AC>°&Ò£'ë}ú%:¿ÒϺ@ÙDhk<EFBFBD>eáöðJlj)f×SEÏ6¡dg06ĺ+TT%QïÂJìWÌehÁØEeà £ »ÒDê) rÜEݾT-ëÎ<EFBFBD>R<EFBFBD>ÛïpÍ9pŠÄz1é{ÙòßÂ
6¾ˆÜ$f·.e£é¬y4ýšˆ^m\éoUF¿ÆÀ?÷áò=t̃ä<EFBFBD>ijíÍ<ˆÊï¹{Á´UGÔCçÇò )ßóhñ⣡9|2ɲ¨aÃP+QPFšù¤¡ÞV[^Hn_W>ÊûcKT%0³Õ|ºSÝg>+p\J<EFBFBD>³æó¬Å³VâUÆFÏHGu-Ø/8
¯´[¯KºB11yQ 5 ?TbR¥užÖb
ƒrbj%)Ý?¸Œ7ÔgA¼ý$w»½p³ÈÏF
é-ÆÖØÕU8,á5ÀXJùÃc<EFBFBD><EFBFBD>iƒËIIÞ*ˆ,êžWPk1ÄEe˜m±²\á¬è帳~¨ÔŸüýˆí¯Ÿ(¨ù¦ êM%»E«ç7ŽüÄM´-X y¡£ø$q÷ý ƒ%â «Å$©]ÁëªÉÅ<EFBFBD>ÀÿÒBJ¦Î$RéQk¿mv÷M¡!kÿý| G3k_â"¸‡ÉãŽ"H ¬E¯ˆ!·ðÀúŒ6*õúü¿Û ·Ew¹Aíçº+²<B<EFBFBD>è_>¬½ñu3EóÎôžg´]¨[$¢Ô³öêJ:ððó:<EFBFBD>¡HªbYžêÐ"<13>Téñ¯°:Yƒß(³-ÝF…8ZÍ9ΟÅýxŸ×3ËêN™Y²áÏÿ"ôè C<EFBFBD>ûÁ£¸,žx¤<EFBFBD>¹˜³&«3 Ãø=<EFBFBD>ÿ+ŽøÔˆ["t0¼sD<73>úƒÃQÞ쓼,Á¸ðkHq'TB˜Ã;ž»‡cŸ8g)¢ÑÏìE‡€ù Þ{ÒdÖ“äç!£¾,°#M`^¢ùGÐÓS”6‰rÂaÈ&þà¯!˜¸2<C2B8>%a0äomoˆ3w…ì |[7Qçs[2åÓVê 'Àï#
÷¹çÎýÖ Ç°ç¨ÿ»Y¸§iIåæÌ-Î 6¼ÞŽÌªíArGþ<EFBFBD>ÿ]k+^eÚ5@=>CððMÿ[íŽÇ6R0¿:ŒÎ2÷eÏÐ&Š<EFBFBD>5Žá 鳦}Û-À [¿ÄÜGËû÷.ÒW$<EFBFBD>\+C-ã_
ùØÅôœbO˜{Íø&Ð3Ö?üH ~<EFBFBD>»Sj|b5fÛril=*¬u7ráÃ>x:£røѵ<EFBFBD>Ë?ÞÄÐ¥_Û.e1¯h¾ºP à{ƒÜFòBC,{7j©|×WiH,j¦ÓÝ1táë #9ì»ÕúžŸ&6˜Òa¦ùó~SúÍ=!Â^ÛÕ¥õæ¿hj&m».3¸OE@U7:ùàݸ5ú9¬?ËÿrÑx/zìe2<EFBFBD>ùY\ª(H 1é¸m¨iѪ:è\§Ý2Þà:M<EFBFBD>c4Øôäf$d(q/U:ig Õ3éö®5
³QÂÙH3Hqœw Ïd»ÚRm4Ôš²Ò<EFBFBD>JËÑ>«Æ©%ºõ¬<EFBFBD>Å;s -ªÔ\Ð×î²ëóçt`^BwPží0)­xPg¤¢¤ºBŽoCÉÉÑ~·úËCc8p,ó=`:E ÊЮü¢÷­Ü¬v?q<
ä¾*-¯g[ʤÝÓÝJ¼q ÕùF(+6¢·%S L¨H<EFBFBD>D{^½âHÔeQ'u ?ðéxIÜŠÏ<C5A0>u—ÞýȹÏü· c˱h:zËÛÐa¼…åÜÖîYÝ<59>&ÍâB¡x‰pÌ“£<1C>„=€­<9löË<C3B6>YZæ¡x…í•Q§ ª#/2ãß?SL§ ¦e(ÿ°t/]Q{PCé}Óu…¬ù½ÿîÚQrèú†3.Ç?YìVRå)%r*„©¹ë6(Ê=L™¹:Àúz*óÀi.d´ù<Þ±œ³ôM™¯q$Ô¤´™2<E284A2>ºÿµÎP@šSûT<04>
ïëµ)=º  FÚËGV-l-6O9^<EFBFBD>g«4Ë(b8Ô¾]§sf ûŽß4Ç~~TŒË¼Å½Ï«­Äï<EFBFBD>ÜÌ[Íä ©Xˆö{v(æëTüKYHr¥Ë?Íÿø\즹'îÑ@ËáBŠÌæS¾(˜ÎXŽÙ@<40>ÙvÈ”…>†ž>Õ”õ+÷\&¦z\ØlˆñÍÔ϶Çø°àû¬êÔñO3q³€ý]¯ ¼=,ùlÙ1¬<j¥{¦þ`¡sY‰<E280B0>…½7òt¨@,j~mѪh g”<67>Z.ÚÇÍXAÂõW d<64>ëÞÊnÀ"dìî¥èkëA,îw*èx0³[dœ½á?Z{î <C3AE>6
;ØøwÂôNdeÛKä¼ïnE û'¥ˆW;W¿ÃZ'´¿µ ºöÃÀÃÿa}ïÖ+þ6ÁRU"páo¶÷-_x¼ÿæ1{dð©-<2D>À{Y¦O>ª¡â6ù±”À£á"Áäsp˜Ë¢­ýoqéá8Ó`­¦°©Ï?èCíVân\VqçapÍJõIîûü&ß<EFBFBD>,ª¸%e¢c E§òf8¥ÉbG<EFBFBD>ç^ÿcjnO¡ ;ëÛA¢zpG¿J/Ôr«¦<EFBFBD>ó4¯¾ðüË»øg6síûý÷Èêɬ¤´G=Û(ì. ¡2©YD@'8W_ DÊÚQµœù_­UpoÕ©BÕJJ#dB©®-§µ¼·±mþ½6¯µD@™>âŽHh»è4wû<77>ýãù&¸ü}5Z!<)°YŒS®0ÆIžù<E¢ÅdPá#Z<>¼¡œ§•g`sœ¸ÁƒÇ
C<EFBFBD>Ô¥¾aÙ3:q˜R#»<EFBFBD>ÈtÔIÓmMŠ'°<EFBFBD>>ŠõázSrFFr-pÚÎàÅE,4e9ŠÃcnÔh认¶Ld=dG©õÿñ¾é8£?WóYž÷ÿº£ª4.jØÛéD‡¡„ÔÝÞ9Üèíª­'¾ ÝéoEÅ~IOJAÞ¾} /Ò,T^i¹+>ÈG¼^`U šìíï_Àïde@2ªÓ®WòI¹Â¤kk×A¸ÜôBCÚÏkùÜ<EFBFBD>bîr©ÿ|<EFBFBD>+(æ!ñ³tãCëd¹©+>;åÀDmÛ³] N^Š¹\žw]¾Ñï]S<EFBFBD>´åHÎÉÃ05|+¼2ö½´,×Ñ<P»Át·#à®;$z­˜mO·Â,®9èCy½^CójmsìȪ©D| ;¤<EFBFBD>­b<EFBFBD>8d ¡$+ð¿íw÷« Ý:v°ïØï>ë漪o˜ ?-Ï<EFBFBD>q¡[h^@=A¹L4^\×^³ÏdcEVõÓcµU­ŽÇÍÂÁ¹SÀœ#&}δ¼¥¬60ô¼PuâIÕÿVñ^\íòB!íÁDJšeõÃ@FíP©+Ði$smcOz<EFBFBD>Ü ¤|¤^ΰU3V}NZ#$/´ ^ê%t¨opïVVÌžï@oþÑÚvÐÚŸ'õè˜Â^÷r•Õ#j±&)Z/Þ=Xyìž<C3AC>48P<38>€àJÜÎòäúÊr8mýÕEyõõ <> ò¾<C3B2>ÿÓ©©ÓÿºýÅ}´SŨ:óÎÍ<C38E>à‹·àJ%
c$<EFBFBD>þB !o>¾<¥z"(HOF…êCKfQw@<40>N×HÍž?š)jiBˆ7<CB86>œ˜ãŠ3þš%³R8ål´“οàÁäÑ¢nþµÚ7 ñ7eYh/2Nd+ŠíRÂ/¢‚î%ÝC&Ð.™rÀÁhöð¬QcÕa­Žâl_os¼Z7ºCüš±½@k`±©ÀKðŒb»ØËÃÙÆ:ôl_ Czoòûˆ«N]Âãb•ç³Ö]œßJ¹$êÖÇMª@×x”¡hÐTóT=ÙÀovv²°Å³;P©1y¶%Ô_ O¢ª®™€ÃÅíñ—JÎÜë×:7ò#ø§!BµgVýÑ_ 'G@œ<ƒ8oR±Ö<#‡NSSHËxò&épÝt s“)E«øÝ~ͫȨ”J¡"Þ㎩LÅxñ¿ÍÈ Þý̲áÓ[ØyÝd)ëÒyöðY1ëLv9º_¯ØL'&)Mõ¯7ʹC<01>9Ʋ|TæäOc&hÚ¨bª»9r3àغšüä9À'ÂV½î¦av'öBžjP÷°/ZZÙñM$1Άz!D­ÎZé1aJ~¨}4ìmsÛ®«k¶^ã]krmg8Zt<5A>PGsrIu<49>]K+ºÊ†¾Yƒê¡4µQUux˜ ÏÄ<EFBFBD>ê$OVp¤Ó+Sn}dÿv™k'¬&4;60#=Y<;>ÄdÏä¿uNŽ,Ñ˸˜ì¿¡©+ÿý«[,¸à¦Æªn1sÌó°9½ŸOeªQi­]2~éq1{øëÞ¥(J­þ60¨,²:z~ÍIYë$Ø!Œå[yÔdPmeV ûý,è¾Ü`ü&eóªªU0¼=/7»^ä­*ò/, ðÅ<EFBFBD>bvα4=
ux\kÍÛ7(ÇÁŸ<EFBFBD>˜oÝa¤¯|x~œŠ;¢<EFBFBD>¿ªÁ%Ü¡·æË9«¾è?ˆ<J°½¦qÃsö-/D.äz3Ÿà¾%*)+8(Ý #`³¨2L;f¹;zL!u´M]_\Ü¿³h2 #ïÕªƒ˜Ëɳ<EFBFBD>Ø@ªÕ'n/Ý4ÖWÀ…Úìçtµ“úÿþ@ùvî{Öü†…Òã! “VT÷}Íâx
ÿ´u%óÄ»!§ü=¥Û<EFBFBD>ûttCrÜ©<EFBFBD>ïfBÙ³£°´ÅUA¼@Ø+ÅÛ÷Øûè¹­ìÙ<ÖÕ:7& r%;h!z.ATú¥IVjP³Oï¡<EFBFBD>žh&<EFBFBD>ð"<EFBFBD>åÝ„3ŸéBµ ú uiŠÒÀ³xCý9vUP{Ö@xôhHõñáÖœÁÐ<C381>iCÔŽ÷ÜÍ4+)ùž:¸<>˜¸e£ß8´“¾<E2809C>œÀ$͉xyÜß<¦ÛJ7½Å®Äå ûjן3ä¯Zœ" 1ZÿòvW>;N8}-òYVkS 5x,ãñ <EFBFBD>/S˜Vð4D;Ÿ9\ÂÍ-0Ðè;kÖíˆvqpt¨Í  Žˆ Í°£ž¿²­Öšs#C,>G¨[Éï:äîT¥²­g'G w¾;_7*%$%§iL?V^Là<4C>5ʇj5øÍ=Ò§¿i×éòStí}ó@>‡ÄÜKu}0¸<>4À.ðÓÆM‡(nKëÞÙØÙ[sÁ„|ÆMŽ-N;03\Vì“ÚšÍRbÊÈc98gd©™ ¶n„Îf¶u=<3D>=œ9T¸¦ÈµsüÑvWr¶@nµ¿á´áËÀ½ëw^ŽÔWa|/:ôã&¶·Íÿq×fgô1M '8¹Í4¸DM`æn_j?¡e2±kiÃømŠõë¨"æŒÙ¡/)¢<>Éú<ñ…릈ÕCÓX`1g<18>ò N'mHù ½\¾rñnÖ/8x<38>fM^Í•NUZ€_¯PèÁ.{ ¯¤…!…ÜI¼—ñÖ@³ÉÖ´mR05×< ú¸õÄÙÚ]œ}Aú9\><3E>µ/ÓÓD‡Æ­+—'<Ñ-F=Éþ€Í¦KN•Jk£ØM^;…Wï2½T‚·ªè³ òA\Í¢iÌÑá«ünÐ~!l81õ?ä 
ý7£ä6Ê4<EFBFBD>³/š) ³\ëp[~æ?Hˆ<EFBFBD>îý®<EFBFBD>90tÑqòþý6ÉGê5Ì<EFBFBD>žê¹0G~|½íÔT9#<EFBFBD>|ìDz î4z&|° B¤±ì)·ids¢Äí°ãhxKDtféƒjðÝ¿>&x»zÔãü^ñ° Mw˜e<EFBFBD>ïž$E)½6><¼<EFBFBD>h*ØÕYùåI<EFBFBD>âÑ{œ0Q"‡«-E¢d?>GªÈʾ·9>™Ë’ÅÇú<C3BA>b[‹ª}«š<>D²‡N±† <20>¸­ãÉÈÑèõ4¸Š!¢äÚØ÷æçí n.©}+
UC2Åß¡¦øÒrŽO² ­nPƒåáÇ}÷º$tz<EFBFBD>{4¢w>¡f%Ê]·;¤áƒü³q¹ÌRz]+Ò®,?W{æše·bsRÕShĵ{áÐ(@²]ò;+ã®xo×~^O<EFBFBD>¢^´yBqþk³ÉöéânlœLfS$Xù¦;èu\%ÒºÒv«åBÙažgatæb5F¸ý¨ŸÅªË¯ß1¿p²þ©ö÷ÆnÞÅàMx$9Ÿ·ßar9ÍyÃWÜÿ`ä?<EFBFBD>µñDüįF´ô±öÆ<½HôoÖTv,ô;ƒuãB=õ´Ä2ŠŽ8E·;gHœÂReÅBbüYˆhãs¡üq5uSlÝ_ÂJ<EFBFBD>鈌˜ÿžä¹¡×Š`_+<EFBFBD>p7ÈàRý[æ8Ü¢ãÃ<EFBFBD>ªÞçI<EFBFBD>¤¾FÕ<^C¤¯~-µÉ0´j^2Sm­¼Õ3 ñ%Bo/49Uÿ#BŠZòÃø$B>Ç ýãKðtŽÁŽÿ:HSûÙË[d²í·â×PÓff]æ½_ˆâ¼bŠaA«ÁÉ<EFBFBD><EFBFBD>Óc+ùP¯ñ?`Õré̽<EFBFBD>4þÑl, $z$Gëë¿ò_<EFBFBD>0Š˜%·¡ (öHÝæo;⾌ãB²Ëø¹J&[EN^<EFBFBD>¤Z/D?]¤ú êÏñ´5ìãr|Q&Ë.G¢ß3]˜-àïçÀ«æ8ÍÖ_+Ò®,?WxHÆèù­@ƒ¤æW·¦ ¨32n<EFBFBD>û_@3¡
a¾)[}¾}æ+¸ô6{ï œ,'·åˆP‡±¾6{rðüÖžl_…ùþGÚߟcª,{r¦Ù€.xà¹iO²ˆ¡>ɘp+þ6ˆ7½ÞÂ+VO£wVØÂoBh%+³yø2~^ˆš<4·ƒ¦½¥9Ìæ3yBd?<3F>úÍSíàëåÁÞEb¥z[W'CqˆÎ(|:¢ô'Tfì,§*PÏpñ{…½úÓî]¦sØìƒ<C3AC>Y)×Öaxž¦ÿaæñI2ú<32>çÆ}Á»YÕMÀò8ã¨!Í<>îpþtçB_
Õî-2Øß¡î3{}µB}×DsX<EFBFBD>~f5\·¢¯<EFBFBD>~:v=6y¢ðàÆño­d¨¢0<EFBFBD>Ê.žÅ¯Š<EFBFBD>Áã ×8ýç¬üò0 ¿Ç%\L<EFBFBD>I0ÿ&:iÄ1|9[üVlÄÑþPã<EFBFBD>ÛEjÌ°õjq %T 3þw¦3`h«vÊNvÊwÁÓñm3-÷&Qk!IöÃt|œz
U<EFBFBD>:h1*¼k´ðK^z|©¸Ó×_×(V¿d$ÃËÈþ¯$¢å<EFBFBD>ž ⟳²³5÷ÚÍÈÇÈJ ïâÝê
æ½+ú5 qhÆéòu<EFBFBD>5­+_R˜3ß
RøµÇ 7<ÃázÝŸ½ü±§þI<EFBFBD>Æ©œL©Q2¾b%켃ë<Íu#ÖYC¢¸ñ
mp7=MF!Œò±TüÙ»âShÿVÅB§T<EFBFBD>PTH_YÃPÿïý°ãÉ<EFBFBD>ãDÍÎÁA¯ÕÁÇ}³Úä©<EFBFBD>õ.Û$×Û(µ÷. lž¬¿©Ž_ø59C$åóC\P¹`^µƒ,`ÈÚ¨.G£¹øƒÍ¨ ­p ¦¬ƒï׹쟷Ë÷:YëîäJôÎ{ ØôE (=·.~ ¬ª5G½À{d´g1­û×®R_áe瘻h¸<EFBFBD>9R¦ ëÄ<EFBFBD>ÎQªùÌèúþÀÒ®×âÕÐÏÐR÷êåäåg) æGšvYjcÕž¸eVÞíQ÷œ´@žHdüR 5=ð^2Ÿª°#žî-¦˜׺­¨ÉßÒçOnßê²;5F^¥LsF«g¿G'ÆmÔg¦jº„;q„1Êi<xWy°vbv ÍÏò ã<>Ù¢¶óÚÑ<C391>Fvèç²»óíTÚ<54>§¾máƒLÕ¹1 “ýf4>ç͵â`œz{úp®óì~¤Ž ϺN~g<>†Í1kòc;bHc<48>äEíJ³wŽy<C5BD> eÏÐDäÊ<C3A4><C38A>.*¹8õÌ[²á´4Y+ùÉ,vymo®Ša~m ƒœ´m?X•;ae“žðÃÎed*­<>[䆇[v~<7E>Sä)À±wZ¨y<C2A8>"eÓŠÀvJ¯Ö÷Åä-u ª¿³—¢{½K?;m)néëcq
!ô{ΈŒ*õŒÂq|Bo<EFBFBD> èÛ
8!jA:«lHzy*Á¼îKDŒ-ÅbŸŒ&¼8<EFBFBD>=·êŒÃXáŸä±а£žÑt¥óŸ³Ã¾ì¤Í±Ø¹Zrâ 7ì±Xþýþ@»¡Îu˜Z=0+*«·¿È£ÚÎОàµÆJÛð13â·ÏosÙU¬WžC¯ý º¸WcÙÿÚ-ßاٳ` T)ˆÞ÷<EFBFBD>{PIL+1µop4GªÔ÷M<EFBFBD>`6pkòfœwZ¤cûw÷p'»°Ð‹Úã¾K .¹¥é¨@Ž‹Å¾ÃÙÆ"úUôÏ$ÇÚ§þ 'vAûýZ½^r24 $Ga³}yc<EFBFBD>ÅG0Å÷ÓX_âÈð<EFBFBD>ñm|@Ž Šå1<EFBFBD>#þšªÈ¤ât×é˜Ë]âô-¡ÖXó4æ·Lëé+y½ qI<§1É[1-J¦¨35ù9'x†“ŠaÐ\—Üsèhh„h1gŒ¤ÂcÑRéì3´™„ÞcÞo°¾²)¶Yù<59>Q†t³îN´°p§VñªvÈA¢À9¾˜<C2BE>m}­Å]2¡7f\ ,”¯ðeÎ<65>ÎÑúiÞ^^z^'=ɪßÏ(úÈеStÏÄ­ôþ½¹SQÃFÃôè2]Ã
à)ʨ¾¥ŸÏ¡<EFBFBD>)<EFBFBD>K<EFBFBD>Y[8í?¾ÖH)ORJ¨<EFBFBD>qíI]$§<EFBFBD>½ÈôÕá÷£p ˜ {f±QˆÄ-bâž2óÒÈ îJo ](aó8ŽF"~‰:ï†XBs-]:}*<Ñ’ä=?ùžêúg‡zùl jýê"sZe<EFBFBD>\?º»Ç/½Ë#ç²±ÜøŒ|IëÆðÊaÜ+Ø^îè;pñã9Rÿ½bœ«1r$u&Qm½Ó6·g?Ùç)`¢ÇŒXq«ä|ˆ˜=$ŸŽ+²ÿ3<EFBFBD>Ú1°ÈP8ú=µÃÃA{PÊÊæ<EFBFBD>f Mòï~û~Øú=Çá˜ÜÂŽŒÜ:3JùSI|Q«X c±áacícwŒwsP# ùmÀÿ´êcÆõ¦GÁtÉÔWÌB§ÞÚï¤Ï˜Žý_Áôj`ýD ;úŠ¢P"A4!Úá?Ä^3Qqƒ{µD$c#@X°Ì|qƒkùÑŒŸ™"à íÃH§q%o8Æ®½ÁØú³]ÿ^ìÎû´ÞüýR«÷¹ïù{[;+ˆÉ«Òû˜JC!¨Wæx¾½}| oŸ|Ùö±í)cp]S<EFBFBD>½Å|<EFBFBD>ÇÓÉG8¸×!X­¼UV¼ÿšÇS<EFBFBD>iæèmñMB~Ì@~ñ ¡~»ËΡöM¼[Àu lÀlƒÁhˆEÊßogÊX¦<EFBFBD>do¦¬9OzS§kÚòì-:0 m.¹Æ1ÂË+ Ñêg;úâÁ±9mypŠzžÿ!ÖÄU"ù5\Ao9Ãs68 BÝèS^R3ÆCœ¥"®2«¿qy^u ±ú [ææ+g_ñ²7¼!ÂZØþ'Weaè^»1¿ÙõÈy† ®HÎ #tsL¡* <>DQÓƵ9@<40>AÛ~åÒ ]<5D>¤w‰nç+N$h*\‡Ç¢•<EFBFBD><EFBFBD><EFBFBD>Ô·ª¥¤¥'Éø:Ã%ÒÍ{MžèÒTvñóMW/h2×öÅ(oGiÓ$5þ<EFBFBD>µx&ÿGµQ´vYÓˆ~Õ ^YXYÛ<EFBFBD>snm#<EFBFBD>( IsœºŽ«ú7­1²Ëç=£Ö"œ7ÜéNó³ K${µ'Û]Xÿ/r7Q<&ÕÂ/uÑ÷X4Ù=7r±}_ö2Üš2/ •Ø‰¦Úç%˜)Îù/wz‰±J
ËαßqHrÓÔ8ŒP(é¨ý¼£_+-'ñ¹ÔÃÏÍ-;<3B>;dWƒ8J¯+8aZ¶Ä¹8pµº.z€À©nÛÑÕ!Ʀ+¹.)ø‡<øKNÓðzñêYªËßJ3ïŠ
žˆ0:M(ß[Ù/a 4Ý<itý^Å°hÑtÍt+óQae,s\kNjø*®wˆm=('ÏÙìÇ~úxÎüJI<4A>I½Û,ÓÑðšáy[äRh,õYu·=i%ŸB­)%InCƒx^µ£YdáMóÕí—X˜€#O™Ü2B<8}µªêk ÿ¶"c]ÞúÑ<C3BA>Ú}XÐTÏT?Ì=HEþò Mè†+&.ÖðtCZ[—å:<3A>q"~×DüX­/°`Uö2>lÇÁÕH^b^l]`¨UKkN„¿l{Ü7·)Ñ6oÉœ—À¦É›é—xЧ=áWÿŒ]ó÷|âÙ<C3A2>ÿ»Ám“9ESCýP³…Vf†©t¿Æ´™~ý_"MÞTfÓ~§Ö<1D>“z<00>oƒ5Y¿¤Pÿ¤jŽ+֟Ϙ˜árÖÛurÃgRÍ8ùýª¡ð ,úÀÀEÊç%ÎÓ88?Kã+® ¸`ê=fÔI<C394>[ctX=”®3Ü+±<>Ÿ€Þ•¥f•
ïÕŪâç)¬ÇÀøÏýºJ SŒ 
êñj>÷JeO<*ÞFa2´P~·*ls¤È+ACS,ƒ`«^N²ëUQ.PgR|ð Âè#Øßö¹aúŸ:¤'{ˆñµ PÒ'ÌJ`Vl/š<EFBFBD><EFBFBD>F­ƒEXŸÍö20R"á6Å$)a-:¾?zœW<>ѵàõuiƒm´'¦¦<C2A6>#-—9ž+7&øá¯wØOÊ<4F>tÜߢîqd vâ÷HôÏ• Æ*<±Ò 0uJid/ÜY\¥!ZÊ5VWðG˜ÈÉ<C388>ŠžY<C5BE>¶»…Dîð<>ù_Ÿ,fð£OQÝ…,KµÐÕÄÐ&Æ\ñÕÐÕÓvPéÁéð ˆ<C3B0>ÿKG0)’Þ
ÜuJÁ}».wNÀŽc˜]>Z3f3i¸ÂÊ5h5þÁiàŠu¦¨Ïj/Œc žœøÑãXð@âäÿ T5òô 
Žz ¨«Z-žjB"¦F/:U}B
¿ªî1tuŠJqüÙ­<EFBFBD>>¯u} £¾æ ·ì­û<EFBFBD>D@c® #ùø®ø¨[¾ 9åöS H·CYÏuC xc¥z{;t¦HKöxÏÍÿŸ¨°·^UorYtø¼½`«!¿TÓÕx!䈎-®çÌ(¼£ŽQ×üJZåb¦mEw]x®Îé8y|á5ázB.Ǭ]"¡ì¸"ªÄ°öTóºõ<EFBFBD>cm¨CsB}z;¡+ê£÷Ѽ »©©ää²9l¼'ƧÕKqà ÞèÓæGÛù^TZv½[ë'q½"ŒI8|¯0·¦=•NäÍͦŽi9°I‰ Àp<EFBFBD>¹st.ú±N:hæꮪÿ"¯À5RC`ë<˽ÜÃmÏžÏÔ>Û­è]¤þBe`6áXñ1µ²&ìH+_!ܬRײzx¾õFf:Rfÿ÷<EFBFBD>[|Àót,·1ÙÞæí¥¨Loç[¬1?HËÞCÄ7Õ$Ø)M=0{úù-<EFBFBD>l)÷0˜óÒžJòÞ|ñÛBÞÀ I»Â cÿáp©ÛEo³¼R <EFBFBD>Œ<EFBFBD>Ñ´§¢¡¢$æɼ··9ûÞÉ1Î<EFBFBD>ÐÈ\üW¸YÝ8 oR[ c=iËMÝ ¸{)E}2dZIDCÍpòÿkVØš}pkjkíO¨×Û±ÐäݹML<EFBFBD>q¬dø.Yp¬ÇHªëĨgÜ$1m-.?)ü*i1sm :ÃP²% ZàACȪ¾0h7}Fr^y1CœøüÖç'<EFBFBD>·oøôÿ€‡Ù.v É´<EFBFBD>À»Ì&êýØÀ%ÖL<C396>E9ƒ¼VùŠø(°"ý~N=¬ÞÀ“ùõ±c¬Y¿½´èBAµH¾¬ršXydšÐD:²{“-¾G?Gçy¸¥á:¡Í.­Ï µ4öñ ÝfÍ:è…ºQÏ]‡‡ŠÇch³Þ¹[äÒ |v(¦•+¶r@=I9íÄ„u@}¯0†îy˦B¨~ÕPÀ‡úÀÀ¸ffŽ÷°&(u0Á̽ÿXm'Éø<²ÔÚæ­¹!?5 ~^z'ËçÔ ÞP@w駶Qõ1þÇ:LLò`²|€ŽŸg†äp/Cî¶anTØ%þïÓõ%’¼Ô¶2IçWP  Kd S 7gÔþÁrM¿Ô÷¶Žy#êÓÿr5øLåJFëJª0YŘá2îÑ<C3AE>4¨%k}˜Qø<¡<>¤&ž-ÜMâÅ’aH
QÓx´§ýà.ïïD ôp> HTt'.ËYÞþ]'* wõgE½ B;½ŽœµÔaœUjÈóÑ Ù¥àš·tÍÒ&œ´]¤wÂüèÛzz¼ïÕ¬Öþâ­ä²ÞLÿ£1Ö5?Œë=.3ß}¥3ý§Á@=ä>3PÉpZVåŽü½õàcóÊêlµ¡T«˜&<EFBFBD>ìÌÜ0 <EFBFBD>$ï*±ôò{ïBÒ¡âÙªËâ­õí Ÿÿ-¿M{THm ô½ÒÅ7TD,¸ÝÒËh èÛ,çbàqÆŒÏ6X&k
tF{Ÿ®¯{¬EXp^»ÝñÇC9+¦Ç¹L«åG¦Ö)ûËÐÜï*Ÿ(y%:qH·ÒʯÊÇ~;ÍLg½e¯þÍòâÈhA˜4˜ï\¾®ÌúYÖÛü@v³¼]ùàŠamš¦÷÷ûƒmMpÇœÙRæE..ôÿÇÿµÀ<EFBFBD>eE3/M!Ž<EFBFBD>,¢6ƒWæøàÆ<EFBFBD>SS˜iT¨Ú[-ò6l©@ϲÃK)±y[h@"³8'Ôkº ´<C3A2>I<EFBFBD>×& " %öá5¦C«HÅiíÐݵ({­Û§Ï]¸š>ïÀ ÇÄc°úñ4}ê\ Iôcbÿõ@¨îÕ ûG ÿAQ7V$ ¿1YWHõ±ÛìnxO_VL/ý\!sáem3ï(Úª J!²¬š«÷ŸwI×;ÝÁ<EFBFBD>ÅØNæÝì"ÝÞt_G´çÿ³# :$KɱåÇš·qëm±øo<C3B8>¹ÐCƵKÖ`]柢Hry ¯õ¡,¹{[ˆe¨ÕŸ²®©ëûΨڿÆÉw*Ðè’ÉÍïÞÙØÙ[óîíîp2êA.Ñ7 À Š¨zT·Â9m`æ>í[óI„œQg `<60>Ðgqì¤Ýu÷¹¬ÐÁúÜ<C3BA>Ñ´§¢¡¢$æ9âŠ$=4EÊ—Ç`Ö@üv«O|¿©ÈüTP„þP[l|+¨|a¶$‰¥
Â/A4w´V/¨ªŸfye\}d)¬¯<EFBFBD><íL>­«<EFBFBD>©ßƒÈ½aêõ:´Æ3Þ<EFBFBD>yGf<EFBFBD>ÁÝ"%kÖkï .9<EFBFBD>ÙÊ$.¡u?<ž+8jsw®6mRI[¦Ô`²ÓÆ£—fëÃvœ£j¬X¾Äî´ŸÀ“Y7,° œ±ËÒv!òàÿ]ùÝ•h@ÜÉ9aòöµ=ÖÜf9x¤^Bgˆ¯"z]J94[ÿˆØ,ôÓ<EFBFBD>ÿN°¥s<EFBFBD> ²Ö±]µ
âÍž,±¦·2ÑàO Y¼ÃœÄ¨r±iQ¬ã¢¼.½¹JÎþGƧ¬Ü,%<EFBFBD>%ü)J:B`À0ø¥X½ÜÄõ<EFBFBD>Æþ·a.µQ{çZ5É­ÖÎCL1ËÁDj g!ÿðW?Û8ÝZÑ"È(míî‡û¹“oá³àS±ÐAq—ü±,o~ðV³ÉJ<C389>XcHУâ…È}-9“<39>êǯ=¬!ÜpüÚÑ|þB{X§0*¦êt6£fúóWr6Ò¥ZL$O#ÿ5kP(ø`a*²su[VPçRû2`u:컓î<E2809C>Ø>3Ó_¥Úˆ—¾bëö;µÇ4ß ×Uˆ4
»èz~õôŽ·üŒÐ§|H<EFBFBD>ø~üa;2IFt¹¼¢ocÞž<EFBFBD>ØB·ÆÞ±¿Ä<EFBFBD>2ÞŒˆ°Íô½Ò£¯&5Þ»I¤ØNIßãéâ ¤ö"w2œ,&(Ay$êTJΠ*<2A>ž-:ºÒöíÿš<15>×7(“ï€Ìk”h5;|›Ü;nþg†¤
O±úët¥?¨h°¾¼²M i4Ó-ó/B)S&¡s<EFBFBD>§ËE¿yŠåK³Ë¨c¢?£ìQ±ÇÖ"<EFBFBD>ÿÇmv°L¸øEK*ª¸-^Ñï{<7B>!Ñ&BôV$òü\Ÿ;t‰—¡pbÇPf”ùœø¯ü®»Ol«“•h\<EFBFBD>¾^C‰.<0F>©ähUSÑáêU¡Ÿ·þg”hŸ¯KWr;ØÝw 7Ta~ÁÐB¨-—oéªL4î)×ù(<28>ö¬4¯'âôx(zÁ˜<C381>r!¢ xD X-5-=¹ÓQ+?9í|n…·Í W^*ár<C3A1>v6<36>õ‚@{<7B>b“âc˜yžç©á<E280B9>À5yÅÁÍÖÆÌãòƒßâÂìÃóI@/ YiþCt?6Np4Ø\Ã::)ÌÊ2ð8ÆGH'Ñ‚Ü»¼+Îf´BéÑÉÝœ²ûŽóô¸cs¯­nÓ'GÐaå }©$þj ŽÁä7'¼Q¼ã<C2BC>¶XáÌH™k ãj 4ˆ½Ï:ÄÜ[Χà_éPjÏU><3E>é'‘ò”#n7;@\“Æ-Ô¢º$<Ójk
º5ãÀ@,hPÊþWêH÷rŠ1o¿<EFBFBD>·dÛWWLd+°8/ë4#iJ§¦i¯¢Y˜ŸnÍØØw*ž
èÅ¯Ç ò{µ;O®²çÚ݈âþ¡5DbC£<EFBFBD>"^h5äÈI-,½B4×—Pþ¹<C3BE>Þº}}rŠ¦QÌçggqŸêm­žþ×}µƒ¦øŒ®>2Xâ]å#B <09>$2ù¤qPæ:Uiíl­T®ëS$&jŠG™ôᢳQ­ÜŠ¶ÓGŽ<47>@€ivhõÌ^<÷At㘫úÏ”<C38F>—Å`ÒX±6<C2B1> .òŠÓGU?‡4e<34>äÜAmôýóDè ]ñvqÙäU:Zd¡à(2Ÿx£é‰]>ü·õÁrMÍ9éiÜ<1C>»#ôëq3ŸKBàGØñðÊ1…ÕÚ½¨±±œÏ,;_Y‰Ï&ctÙ²L g-”o}½­LvëÒ9v<1D> f9 =Õþ<C395>oŽd¸UN<>`-òæôtËö||²mmÑÙЈE¾ªõ J>Ð5a­äbZôÙ_ú“½ñmb<6D><62>ƒÞú¹NKA¿/z°ÕŽ)÷o^ —êWí1³Ï®P]µVT^a‡¹“"¯Âz-oy7 ŽËËŠj<EFBFBD>§_¾ålàº7¬M&M&²Q.N Œ^Ó´¹®oâ_¸;@É /µá] R~7B÷Ò8Q<EFBFBD>£W´Ô}Ï/ß/&%´/7×ä³.Liš¦¯¢Åx¹äL·ä䦺M¿Ñ`ú&p`{ççž`5W˜ü<EFBFBD>4ÝÔ¢Ì!öÒÞ)áôžžÇÚ÷¥#M ððOÜ!õªaAt~6bAÂþŽï»#÷3!Èã%ë 2œÕâ&öm
bùuG÷ŠëÓAO[­,Àö4;+h9x»l*°6¾t_J <EFBFBD>è´»¢ñWlµA1<EFBFBD>åÕMú)©ÃG/°ëòR,¼]IDóØ܃~yFæ4ö 1T^<EFBFBD>š2npJ´2ŽÚÁÁ˜{œ?Bö5Yß¿Ç,8Ç g]Ýñe|ï_oÐÔXOYÿ¹ÌÐD®a1¥
.2­ZA"Ê6ÛäÇpv ÌéžÝ‡'Ÿô,°ð}ã<>´Ù*h”1è ˜ §ü`V»ÄÄ<C384>ÇmK&wœæ9ºg6_K&fƒjɺ7«üCÏY‡Ë3äSÇpgfÆ™Åc¡AA與çZ^²5ÊDmµk~~o R~œ‡Ú^+'ŽêHß{¾žBcó•o~c•5÷GT&,Ç芌Y[zR¡¤<C2A1>€±ÁŽµR¢jp¸j/· :ýt…F £¿V™Bþú¥àÑôªHfS Líf0eŠUÄA†]2÷ØÛ¾;÷e|K½UXemCïå«Á}ã=L™˜—¬Žþï{¡¼ŠånÍɶû¥¿Ì<C2BF>ÉWØYàei
%;üÙ¸ÍI
aU1мÛ" _ŽUôA~/»é—ø#vÖUÚémzÄdb$îB~%yrH­º”"Ò¡¬-t­X´ÌE<EFBFBD>ÅÁ¾×Ü£¤ñ妈&û@î<EFBFBD>Yãg6<EFBFBD>DÌX?¥êL & ZEÛѤ¸àÄ*ŒÜÇvæù\§×ºSmÃ#¢èh.©œ ´èÚ½ß ÕãÑÑ?èã<EFBFBD>Ëÿ{¡}®ÖºÓH5ȾQW}µX11´v]=>9¤OÖZR6ˆ±l°ʺ
lDÙ+îÇ·äÌ4i¹öç<EFBFBD>B<EFBFBD>ã^Œf0<EFBFBD>)+;/úû¼O!M©1ù @ÿ"Ï<EFBFBD>2úÙHaLüÚ•@ˆö<CB86><C3B6>€zk˜\7OØ—éªï6¦l_Éå¹´ªuÕôÚaµ´4D»Tl¾ËÌŸqüSxër¿hìãÿI@vP<76>º1³}!!Vçò«˜ÉHCep|rteÚoÒÔƒ*“£jæýƒÎS<53>v๩Öê¥TF—¡Æ¶777gP„m•ß”ài®Î©òx<03>ìú:3ÑÒ…ºÝ~„toì0ÛYÜ“£<E2809C>Î,©=[Š@h¾{{ÆÕ/Œ(~#W/\ž¹DõºË<C2BA>Üâ2T ²¸öRæ#ƒù`·ÞGñ_)íW©à<C2A9>¼Èå“ gÒš ù—vËü:‡Y€ºäwuLñß—<C39F>ž ®äuè2Õ<7ÿD0ØÒ6øÛÎÉÈÉK ðãÞÝÞ`"øóòóu7õyF)Ä·Ã ×&B½<EFBFBD>/KóaºNÿ4+3²
^íˆvqpt¨Í  Žˆ Í°£ž×¹¥à¾÷Îi\vÔàâ<EFBFBD>Ì<EFBFBD>+²6QŽâBÓøÙ04l½Ê-fLÚ¡T7*IjÆÂï767¹{^QLKÌ(Øà/¡éÄÛçÙÜõqºj~ŠàÄ<EFBFBD> }ºB®Ûu,0®¨¤è¤ãpÆÀË«ÓŒñžå¾øjUPu?ä4!f#x0S¹M1~~úsqƒ»o<EFBFBD>³ErIr<EFBFBD>Àœˆã·î[0É&Åœ¦¡Jh+@Ñà%È©)ÐñŒÌN9÷D-é?ô¯jªðD4Ê|!ž^ Ò«#r¥Ý5x<R<EFBFBD>HQ \^n»`žxz¢«Ù4Ì3ÿÀv<EFBFBD>³þéöºà¨?;µLÄÄáíÊWèYü«óU©^槧j=»i?¦å¯ç#Ðï&¬ _b>`ÄßIhy© 9Zhó<EFBFBD>î<ž¨ô\{Š'‰”× Î×gAý;<iTöê6b>¼Ý­ïíôJôiòزüïÄÜÀ®)iœ8Aj3<6A><fQ€€ó<E282AC>]OTèZ”®l~¦<19>ÐŽi³»œW»é¨½º*0<>À¶DaÜuZ<75>Š…ˆ·c<C2B7>gçªiðó÷7³ú’üj<>hRz¡^Bûˆ¦†pãÊú@ÓFÌhþþ™Øîm|ùr)Ñ0i{½oƒŒÓùU³âoد¶/9Ó·¿µËѾ<C391>ÆRBüøEuâ힆Š-ìÑ©±ð•4$Ž±ÙC-_-`<É ÓÐGô
W¬Ó;bdi'­¤¬·á|'õ<EFBFBD>ºÒ£eýèªËp×UºzÃDE­Pîú;àü¯#{W nú6<EFBFBD>T<EFBFBD>í¤Ÿ «Â%¼<EFBFBD>CÛZÑÔ±êWà£~e .ÞÔ· G P<EFBFBD> t¢ÃéÝب9¦µø*X"¾dLêXEs)×C5-^ª.ÇãüQ¬H…ÑIâ>lÇc¸ìdýo‡â~¯û>Š„ÿ®~0ŃQ@mÉ™=àžl;‰5ä\ë¨-hJsµ@8 žnE{àPhÆÜ©¢q‰ç"Jp~Z¡M$7±T@ñÔ:Z[QÆçM4ÐX×É<EFBFBD>ÌoÏʾ¯<EFBFBD>]Œ<EFBFBD>NÔß¾Ôh©œ[t9Tê²1#ÑÐ^Uµ<EFBFBD>¤Ö¿ò5ãå¯í ö=jt1uPa"7YÒ¯:˸Ûµÿi_\±TVìŽ;<3B>†h®Ç…Ô§ÿâì"¬·7B ð¼ä.rE2;þû
ûÐ<àÇœ·æZ1Ü¢E{M=_tÉ®»H)É×ÇNXhÎyÉbCDª\]±<EFBFBD>˜­ð¤š8Ó4ÕŒ¾¹øûÈÄbýÞù&*$óÇïÄúÆ2OBÛY`ö~ Êý§ñÁRQ)ÑÛõ9Ã?Á4$GLÅÏFU«Aö\¢bÇs->wWv´o.Âܤ¦Ür!¥²¾RNÉpþ ÅL<EFBFBD>{KT¢o½t3 Ubiž;Á%%N<EFBFBD>SÆf5Ë;CiŽÐ+ãq%cŽ'3 ·ò9“#`Çn$|—¸ý&:¡%níˆâñºB¤I«Ípw]õV<C3B5> e—g †ªMd.&bU0é<30>u€øé6BB
LÎÙó'nì Äw<C384>¤7éÏÌö±¤"ä<>aðk)ƒ¾-ELÃ">ÄÊç}äcê
üÒÞžÞ3<EFBFBD>=ÑŸ<EFBFBD>ç<EFBFBD> A¯Ã*®uùDýoyt½PµVyX~|m·óJ°ú«¥
6±¥4&Ì%÷˜o¦½^ÖÃQŒ`!Š~e¬¸û6ïM-þ
ƒ'*&%KJ/ Ý´ ±øÃëp€ÓðÑä<C391>Š[_!,a1'®Ù]G<EFBFBD>ÏcÃCÚ=Ì÷«ª³pÙðÛãqÅÌ|àåÑõ²ðF²[ì œòJiKÜÙO*Q%>È%/Ñ»ÆÎQ¥÷dØY;
óà<EFBFBD>±b˜rÅ<Ó6íÕ/UÉÄðÚ¡B7]½2Ý@òßœ2°¤#Óòç»m!$.Œ&E¤7J§¯q9ÎZÎvS?bZÎ29ðæòwg<EFBFBD>Ê:´æ>Ù<EFBFBD>Ú¦âb¸ $Š6ñ¦=<ÕyõäÙ·îÚsÀöbþf ú°ù]!5ï(÷ÑÄ7S¨åæveði¹"8¸Ç¾NJR£‰àueåìyÓ ~g©·ìðž<>#§É£qÍ<04>Ó¹ÐWÜÔ¡ÊAß„Ölá*hcóï÷»(6˜ß
ע߽>2Ò*ÚÕ;«?åGÊóÏ,ÛØ£wnaX&ÿ7N aU¸òðÊ6L+ÃøŸ,`y ÍO·ðázVfæ(ôŽî <EFBFBD>ü1B!ñÚ!a^uïSU þ`ûÈ @æ[ «$TPq 4Š<EFBFBD>®1öÏ\(þV#®Ð*á<EFBFBD>Ð÷Ñ!Añ5÷V[7jßhèÄúëCu Æ)ƒ¹="ÑÓÙÐz—ö2"sC zè?é>¦V:d¡Ì¿¸/Æw|ÝtT©·ü6i¹ô´WgÏR°mr(ÑÑô¥àøé.3¬@P¸ú{Å»U=5ak'1¸¤@9¾SF…{[!}÷ãfÃc¨ÿÙx£x§éìÆ+.¥Zó!"%©¼¸¸¨<C2B8>æ,ÔÔ÷'ßB ŽEþÉ'<EFBFBD>ç·1vÀ´« )cè;”—ùŒ{<53>«tYrF^r=D¹n•ƒá†‰~Ä°cº,‡›œ^ižndëŠk]£>œ­ÈUÖgò±äš}8²Â(ñþ0ÿαðKZé[h—Z³£Ç6;µ1xSüè(TÍùØŒÂ]‡ø9;6oA<E280A0>hÂðR²”¨!<21>ÆüFÒ/iÚÏÁ÷çvºÓ'Ü#ï¡r ?ô¿Ïý2Ϩg\ßA(ØY5(z.HCƒŽCÇ\Ejµ.ž2>㣠èÏHvÊ'à“Ô
\n"ž™¼ÂjRÄÁÐPÍ©|Õ¾QÌgŸ°_FåñïL÷LéÃòX°¤=:Ý|*OéÅ00Õü¤<C3BC>°„PÆP<E28098>v€å©(¸ûŽœ—ƒ‹æ™6®| “ŽÞñÌí̶è7¶¥„AºMȃV4õÿîëÔ’!z ¢JZf:ŸcârµHW°£=_±ž»ÈZ)O±§aüñyt­®òwÏð ÓÅ©ã]Á#”¶äee¹À¯ÒÅWæÂqÅ×ìŸä­)ôQZ'v
 >ñ=~© Ub´³¯÷·%_lÃtxpzdÒMô³_o/&Ÿ¡ù­(|îVm#õIgýÕŒf»y#åA¦þíK*CmªaçN7æß{ #®œÿû-µáDÅz=Ü^ÈÙ§¢Za©Ë`&<EFBFBD>²tüÆ°Ó˜?r(¸ÓéÕ°÷r)ç7\Ú@Õ êSùß¡ßûºsy<EFBFBD>®^<EFBFBD>&Z:XL ÛÏ<q¹´t¤`X<EFBFBD>бè}Ó:`¦e6ßæ±pz\¦ãuŠ_éÅEÜ3 Æ¦³'Õ¤Ë<EFBFBD>ëÂ8ýø>ñ|oÛSð71ù†ˆAZ"m OsÆr²2G6[š÷W<C3B7>å¢ÛÀëX¢uÉ"vƒ¸‡Ût—̯šÏxŠ¦;’½ãÑQÃRä%c¦ÙxlÍÐçn<O´¡<10>¾ÊC{gdaÆ÷“.Wñ2>‡]×
?:ÉïZ4߃Y˜ôŸó=ž
íZ«Ø6ÞŸ)J!2¾<EFBFBD>$ÖòêíAæÄÃðî|¦8È´Îtré¤aÿuæÍöy<EFBFBD>d#®uˆ ?T{ïNPÛ0YT<EFBFBD>9 D¼YN×hg1¨\çÅM=7@å} ¤ÝýÃä¢s.&Âj¾½bèd$¡ÆìÝFÝÔŸ<EFBFBD>@%¤5¦@­p+³"‡ç¦²öEDÌ­VäB_®,UœphÚ‡ ú4ü‰<C3BC>§$èdâ¡öÛh®Š»X—>çÀ<C3A7>Å/ÈÍ# ÷àcšò$ûñ4<C3B1>@Ir e¦Aø±>ý03Ú<33>\ÑÞÿ>cÀ<>K¬ãel¢uïËws¨ˆ><3E><>w”Ë™=ßýæÀ÷2÷§¹%¯E ’ïñ<dêð3Ä÷æìJ'K3[Îr d0<qKUà(nªoŒ¡^89SÒ<53>A S˜áçÛ ¥õÅl0¾;v°3A<33>ˆ®X;Sœg‡±ù¹Ä#Ã,ÁFÿÎà+^vRèwé)¶(… i†SI$/Ѷžc~\D#ªˆSd?ýb)t¿<74>
¢àØÑp3ó²`¥(Ü-⽺ÝÃR²¸ÅÒaÑÇÔápÕÖoU]5¨õÄï*ì°>»öœ¸JöJ<ž<EFBFBD>£ÖµFÞË3<EFBFBD>L[qSqˆ_{N<EFBFBD>ËÂ<ŠN#bä£x4°¥f·´µ7ùÜÏÊÉÊLñäßÞßa#ùôó»¢A$ÆÌZÈ3 Š_PØç&¿þJ ØwÄÈm¥h³ÊlklîÏÄܱƒÈ«ž˜[
diMòªšÅkÌç[RÞÎÚÿù¡Ñ]k«Å´ãþK#ï8©ÐSk³v?:hƒwªRÂq]n<EFBFBD>éÛy^YXYÛ<EFBFBD>s ó¦˜eÄóÖçàRŒ<EFBFBD>âÓ˜[jÎŒ·X4ÇžæÔÜSúöïîo5\ŠÌT ƒE(˜Z<EFBFBD>;ÔWDÕpQz»ÁìHv5h{aÜ#â<EFBFBD>ãæ+ßWŠÜ¥Kèš  õ©«6è7윹OÑ+¨ý ìYRwíûÃ!¤lQAÿu%L£HuWP+',j9;Òq¨aä†Í“¹diâ}~©KÃùG²õ¸J}Âw¿b—Õ¬:£c í„:ÄR»A¦£ó˜tíÖôžï uSñ‡=,ú3ç266õ1gÈ"l'ïMXPÔ3ŒŸ^K³YB3¼ùôÀ)$s=Ó»×Â<EFBFBD>4¾éŠ±¬-Ó00Þä@ÍÄÑݦ¨á
tNçVõ3f³ÄO7»<EFBFBD>×&ã¬{Ü6è賩R,³:«Þ×ürÈop2>jÉ šlN䥹!q¬ÈCµÑ@Ô¥Ý×µVn฾iTúùú<·<EFBFBD>ÊqV9,'³»
|ÄŸ~´CQ[|~ÉëÉbp²x¹'O„ÊÈÈÛUeËBà̺©3“ÕÑÎÕË ça"ŧóé+Em‡f·`–œ¡-0/ˆŸ.ÕØ3fGÿbŠ”˜\••vÒc¦Q“;G,7[2²ã·ä<ãf!¹4³«ûÞ%àJK¼Ÿ áócf¹ò@*û¾õw¬#úÁ¤E:*bƒ><3E>c™†ûlص°¯Ž9W chªŽ¨õ°¸a´(j<>ùVîû«9ªSp¡íâRa
?JÑÕñ ë ðNS# ¢[Ö»šZ²[äS[s«C=º!R[±%tv»Æþ`¯f{v+2Ï Ç?/<EFBFBD>nUõNd¨ž¨õ>w³.,ò~B ˆßí ü(;¼Åý?4<EFBFBD>ï¡ÛÒ5%d;UUÇ6=C ng}0΢Þ}à<EFBFBD>H @ÿ¦/}<EFBFBD>@$·ÑhÍrå)Ã}¢$ejžÀèÔňÀ÷&Ö`Ù2YêñèwŽî¾SÿEŠ_ŸÁØ3ÎüT7^qg¬AäirdÜÙÅ{1ê^{žšÍ¦Ÿ¼+M«¹¡¢g¯¡RµÑÖÌÿbøÙÞáwõÌÍÜ5àz('ìÚû ÿ䞎íï`:LA†Cœxµ§û¯„\ÃZïÄ)`h£¾Y§Óãr¦Î =íé¤H·‰;ØŠ$öæc¿´<78>ø,%ã OÃå\IîÜfÈ g/·)û>Ji˜<69>AG8ƒDštÓ¹ù#LÙqJ`¥@dtæ{yc¤iÖ ¥tÏ{F¢ÈäãÁ­¤Y
xìÆÝp/´©ÀÃ=Ôé |ìàæVöòíj&ô§³ö°yß5!Û,HE-U¾S^r[fZ¢E2<EFBFBD>rSçWjˆd«91Öx÷$šÎœO[žX!«ºÝɃÔR<EFBFBD>Âè[¾p.×
òò°lIBOÿ]«3¤Ù05*^Â-b±PåÒÁx spW3ÙMkøáø«£³ôóžˆÍymÙ>^¢«ZêQã©AÞ1´=#(ì¡ëÐï<EFBFBD> ¥ Æ1ùz»~1ó +¸¡¸+ô¢¦è
íH½¬<M˜·ÏÓ7Õ"<EFBFBD>¶™„Šäƒ°¤´È^ó'€Ã⯻[éíÎÅ» Bc`ÌpÿOiÝ?â%ñ=°Ø4ÏIˆVŒ”³qDO 0 Lr/@-:uR5ïRÅQ¹öZ<C3B6>y8^Tp¶4f_èýƒ<´¨úb´RmoÍ¿otæÑØ×ñ,"Ú=?(ä÷îäÉ
Ûc<µ²9þÁ?[w>kN*«ì¯"¸gØíR5'b$5Á=`{ %¶—‚‰ˆÂ~œ†j¦¥žìIHgªF&y4r5ˆCÌÂÌb÷HhùåÁü1þŠAtÎhT8Â!cGa.0?Ù§e¹ÀXqäÓ7v¦×ÛÊ P¥¦ƒˆÇ˜ýãGÒ¸ !Æ­(®ËX2 |{]#£>rT7ù¸<08>Ÿàd÷Ø6âÚ§u·Ë(a}.r »(ÖñÓ®’;bÇPdQ‰èåœ ñSP÷Ýýe2[¶¯N“Ô¿æ*^6A(RCXÜ¡#ÈÖ®#|‰~Æô<>*ì›°ª¢ƒó²SšTŠ<54>LV¶@÷‹öŠ^Yö=Íeî1_kã…qw¸ò2ßë ;)ª;¬S·é¾ùâ}À—êù‡´êµôBæãÈÇ"",@ª´ü
/µ&1ËKœg !­1áºgOnAyPÝ.íœ^6­ç·|ô·óµQÓ ÍiØ <½Äâ7¤½ÚŒºá'ºËAøVŠè·>ð€ó‰Ê"f9#¸Z´û¨9oÐ*t¯û=aˆÁÊšåTŒ<>ÀRSÛ'¯qvyл<EFBFBD>f"ï§Ù¼}æ×µõì_üõÊE”sÏêÝ>æ{ÿ•ë4+&qÓ@ÿ ¹)~;Z%¾Qü×­¶e?Zº}<17>ÛX<C39B>T ¼…ýA¤¯qÉ#š|~©ÓÅÒ¾³]×"«w46 ¿ÄêtNó¸ð@ãòí<EFBFBD>Å? 㘊ÊÇÞq*µ»!0K9"Ę:§S\f]"<EFBFBD>ÖñffÇpxwÿ~ S/ôeœáÊ5ܲ*á
!Â?ä0÷S(4Nz(u.RÖ ¬Ò¸18<0p#zT ÄH<EFBFBD>Á\ïOt\¼ëY"¨¡í¨ôÐ:A¹œk<12Ë #“­Ê|ª…<C2AA>« ñv}.7†3¾ ã×4mÿ*<2A>GÑUæ*k8Zæl¨$çc­LQ[©MJ/.‰‰“ÌA<C38C>_±Êx«ƒœä*z-<2D> 6øa°gÇ/î &ý½€µómV|bÛÄâæ¬Úݯ ¼Cä<¾÷<C2BE>¸;yÃ[«Tÿý“… &¶Kg>ÑŠ_¢¾€<C2BE>»lŽñ[£íd¬ömµ_» Çñˆü=¥Áè1ØY½…èûÞ]„7<*@“FK¹O¢EÝo)mÚj<C39A>PK—úÚ¦ ñ?¸oÀ%| .yóÛ;Œ^è•\qŸ*&¾+¦e´ðóé/™ <20>¿{
Ld>>V®<EFBFBD>ßµV¿dJâ6\M_ ë<EFBFBD>jA¬æþ$öV)ª¢G$²B,S(颵µzLVA¸;þvcÁ¿ƒVh]!ñeu}¨À°®{1A÷Ä<EFBFBD>èJžÜž.â¾ÃšÃôãÌe®ÏU<"É¥Àñvû46 .r¢2Je~,_·5\«MW¥IF+*…auul\µ`ú¨þ}pb<70>Š=•øƒº-% $£¿†®‰ÏÚÂ¥íRȇ JñûƒIêð…_îû WÇq•˜&¦;A¡¡•BNmç3¾ÛOr‡vÜZgXœ©ú¨ˆSì2®‰z—¥<E28094>ûs<C3BB>Ûnž'¬«Èäè5a¸P MÁÂR E«o±¿Wºy(êÂ×­Û  l-
c¸7ccÉç;0ôD=ðdÐnòŒì·ÙKˆÃ{ݵ?¦šŸ<EFBFBD>B®ê8^º$ðØÉ·9O=]ÏͲÑëügŸÕæWõ®Ó+E)¥(W(7Vˆ»êC¦SM=¼8ûÇÂêõùïÕf×)"8”qŠÍ‡ÏÁ<C381> E¼ÇÌÏëê°3l®ìÀã Ô𿉉kÑlí¬ßqèÿû3%æ²:h&°?¿T:½(]/SÊJ¼4|P:\~ðYÚFeCš@†_î·HûÃ<C3BB>'¶þÔuÁç²Yk4V1<ƒ9ÍbúYk6 ñ[X¼«0¡ulÚ}‡û'M@Ae®Wýo¢B D¸ 4`†[܆ƒ¿_ÈyÄê†kŠ<6B>õ< zº2ÆŠ ^£4±Ê®¤®ûDëË•úÉiÊÓjˆäíøNï@è<>ñÎwúhëÄ 6‰ð*ã»t¡åX54‡9? `^ˆ%wA×ßcŽJp-ó÷˜œ¸á»¯<10>+qE<71>˱•ªåºytÒÈ<C392>ÆD, XÕp\éYñÙ¤<>$0ÐRGÎä©46"£Ÿ)¹úß xmêUö×TYÄ)i¨œöy
òý(:{,H+âŸo°à1¿ëèA/åZàýŠd^ÿ-?¬þIÇÔO4"+0«J<C2AB>*u>í¶;?JÚÆ«uÒTõÜãbVÝ$cÌCó¦Á̯$õ³Ï<C2B3>ž©®©Ëÿ¨3qKºê¿R(VP®ã\Ú¤éjéíÆî0;¥®/™Î¸Va#C×l<°HEù4ÀßN.*ž”t‰¥mTc¾ZlûÅõ»ÛÙ Â:<3A>×´hŒ2²íª3vŸšgÇ?‡øð/-”¼C`§é¸G#CÑÄÚ"49!iÖA Íp°²þ^Ÿ@ˤ2\Ð!î䨡} Š2²C@JG#y¤úß7ÙâLEHE0ÕNËÂb\î6b­à<EFBFBD>$dFð5° øJ<EFBFBD>>=Gî8´ˆòaÒa£Í¬\É'ºU¸cíaê-™È‡g™PXûtŸÊœWˆY™'
2ë*â<EFBFBD>L6»±ËÁ`ÞƵa"lÚü'ùz@.O2<4F>p~>]‰F#niº(JuG<02>†XN@h“eTý¦ V«'<1A><>ºŒÏ$~!|t…$†,Òþ˜ûIßÑ3ÎýPñt˜Êš‰9ü$wB£ ‡B…Ø=¤„¶v¦Aýk/Àìûe™ßÓQO¥ÙÄSù[_ßßoÕ¾—ø ŠàšüÊ‹—âæ™{Þ7¸3™Q¾¬Ö<C2AC>ÅÇÏ9_Øòzkfíàš!Fg"·. ßá[´ë[Ryå3Iâq¦¹ˆBkç[svduÎv÷< Œœ
·®žE(L·Þ¯Â9L½C
&˜®
çS5fÌ&wý6ð
&ÐpsýŒ¡fK8øッCšÝLŸdñ½bzˆå¤žƒüJCYµ«î"#¨ðcæ¥Øôö:ŸßrÕû)çíÍu©ÆÅàŽál”ÿ.ö‘÷Jl"ïû9­íf#oQkV¬YNã¥JVáZãqèœ<¬µî÷ïcJ´òn]µÜòc5<EFBFBD>ûC KBÊüß&/<EFBFBD>*ºWûÜâ$C¾ãóòöy+G|ÚÉ<EFBFBD>áÙJ-õ#,´e8é:¢eœžÍTa]) _g®VÙmÑæ%MŠXyÄ7Õ½(,ŠþïsÑJB<EFBFBD>Ó-~P¬ ?ðßc5jË©eƒ=9<EFBFBD>9?ÙYlÓÅ4ï,ïDê|ŒI¸µVsý:{6ü¦Ô=ÄÇ-±ÔDºªyà˼·²à7^ÊÒG ìßÚÙÚ\ôïîïq3 Uì!7ò >¦V´±ò:J lÅT*â<EFBFBD>\Ò:»RW²+És§æ±#¢Û¾±¬«¬.ð#ÑjíÚkvôSÜ^Š8=_¬l7ÿÅú<EFBFBD>o¡8<EFBFBD>Û<EFBFBD>2Á¡AR$ô¯B }©ª0<EFBFBD>eÒÛþd9ŒPÔG{3Ð[ÔQ½/DþÅ­./[æ<EFBFBD>Æ!6çÍ9Ô:=p¬µ»íw+:¬ KN·²=¡ú?CÈ2Òx`ad?a¨C¤z^$fõΘ <EFBFBD>TÜ©ýÔS9鱪"gà].<2E>Ã¥oÅaUÖÚpÕéØýlbÅé“bû¡(ÅÊÁâ‘£.cb'Ž?,VqÂÿCX R%x 'Yr‡¯ä'v¡†™º0€­jÝ7ÄÃŽ(¦Ù˯*ÐYáÀÌÇÞ3ØéSGá{ö#½ôyÏv”!c@ ÑV€E*#¢ñs/Ýëi`NûB"Osö½ë/~ï³à·öþS±ÛŽöôÈ»¥À,M®åøœûÆ%ÇßY¸
ªØ¿;תÈÉnÒk`<EFBFBD>X­*z2G7š{Ÿšgy´X¯T¬¢{èÔð»2dY¬¯iª.bÆõlÍÑf×ùé îà×Þ¤1÷ ÿ<+ÁõÿõúloÐp>y-úLòF.µQ^ŽtËh9><EFBFBD>ÿòÄóÒäØØ %©!ĹᠨØ4´«p_ÆéêpiOùÙí!h2ÛF_vvz>ÝmN×Û½è a%a$[á5UpÈýÆ*cLÒèÚo ù{<EFBFBD>³çº©÷7Íï§eF(3ÅÓHxÕbâÀçTY'¨ KJ¿NOþÖº—…W2Â¥gž2{5\ßF<EFBFBD>-ý”gfh+Òq¯“þy+°8㜥Ta§#ÎÈÓ¿…¹.ܸ&¤O™ýsÛAÈt
÷ëV?®ÝäûÎðÜW¹/Pbk[(CJÊ­¨â«|å§pT°õ4ÄÆâ¹Ã˜g8ö ¨ÎK´¸[¡<ÏÅ!ôîÆ>ê¦Ðšò$:ò-+±-<EFBFBD>ƒ^·2¬.p+[š>?HO <EFBFBD>ä;n'±•e!¦®/vë4œ¥Ùª̼Kœ<4B>'®žíf¼V@ùJ3 ¨cßœ)ÉþƨöEìDnݱÀG&¢)·omVáñ?ÕfUÌziÔ ük£R¥<EFBFBD>ûÒ»ŽõÇ´¯åÙ´#ç°ì+A̵]»¥ÀæZi@¥~òä|¸;#Ôˆ¨$²«ø1]@âi<EFBFBD>ÊMŠ=YÐœtuãéKÙóv¸òåž
µW]¸×Ò°òéô2PSêûÇ[ )ý¯¸êöDéÀsÍâR*¤pm?ßJ4¢S+!:WKÇ EÒœP¡&Z4<EFBFBD>ìeJ½SØÈ[à 3©Ç!Ÿ«ß Ƹ·ž˜@Ô¬ËXnã \\Ìùj h*y þ}Ì@5Î@Ò5p+/)¹íDjñð°ÌjUP] ܸÓüŒÄuäýš·Õ¤O#}ÓÇ÷vsºÜܾ쳫© :q{v± ±²±Xµ°z¤.h7R~ƒ
Ç[j¦wei`3Ñ\T½<EFBFBD>ÐV/¹~¨¨ŸrÕDµÍ£Ë÷ Äú{=eèâßJý<EFBFBD>ñk<EFBFBD>˜)®3 ¯sMã)VõÈ°Ž«¬Þ}öèWMÄÅfjÏ!úpa`'w)ò/o«<C2AB><E28093>ÀÄùÈÝÞµ—À½¡åÁVïëÄÅœÒeËfáÔo9}sf»Güt&.`÷^Ù=OÄå C!|~л´ÈqhX¢x!„r »¯&é£Çq}ßx
±àDÜ-ÝÑÖÔ"­4ÎÙ†½û¶ÌŽÊUÍ0=ÿÛ &âKŽ-P—ù:íÞ.ˆ•G5¤úQ23qZ,l¢5ÜC®äì<C3A4>Ú%Q˜Žr¿Ï~¢Øk#òdó½‡<C2BD>Ÿ}z™cùJ¥²«¯Ø±T7kÅÓÅ4îß]Uw:6mQÃMÂE³Ž•Ð<E280A2>§»o_(Ù1yÄJ[Eˆâ¤Žl,ÚDÕ'Çëüý©¶ðxåÿh v…É<02>5&'*Œ ݦÞÄ?
·¿cïãð·Ð+2:\§dfØ; {_,á÷ÅG&!½¢8¦Õwx)qØˬáÏvß¼ŒŠv¿¢aûcûâ<EFBFBD>¡»"ž° ¥)ýÆÕÿ涿ÊIÛ<49>Îوؚw-€èÌ8ó1]7dGÆ #áÆ6Úy4\<øŠA3£²ÔÖ‡4mjD)qrÑ'Œ<>>Ž>Õ©HÛ=Ñ©SŒ1Aw¾—@ï%Iw×0œþ©#…ñ›Ìy‰¿/ˆÙAr„>KèŒJ|=Ó¾¦«N/T'pÕøäߎ<C39F>Íy„S¶9Bà„¡JþŲì8´¡‰®Ôï¾I" *ßÉÞ³òCî¨ö ºzX? 8KGAî©D¥0>ƒ$¬~ÈfL<EFBFBD>10`âKw¸ŸC2x}yŸžgÝkRú/£";ËãTãÐ55ÕÈ*,¯!Ö ÀtŸ)°I _c=$<24>“ÖåùF)³¿iiz0µ˜ª`>¤P-¬j>saåõ¿ßç sn~Èb®¦JŸÙRº]<4D>Ùêö9òÖÿÜÀd¥Ò3“gü®ì<èÖóPö¤#þ<>Í¡&%<25>½•¢Ü W.-‡  fä`}Žf~P ôG3<47>ûc<C3BB>—jƒ@xÒ<78>Bàã —!“ç×­L!§Éر´æÛDû¡«Ì挫úVìÀXl*«×:k)Ö±p€Ö#î@FnÒçŽ"*ƒu<EFBFBD> DQX¤fDx§²ñÆŸ§ç<EFBFBD>¹œùŽ)BIjÖVùGØ8ÓÎa×ýÖù_<EFBFBD>êøjt!Ã6ª1-Žp@=ä£)*G;¥@&ÞÜ $<EFBFBD>ššuø²åt<EFBFBD>¿i¨Ô'¥@Ö{©7‡±Ò<18>I¼DB}Ëð©Ù ëægM|Z4) ”_Œ ùTœÆ&"Ážëé]74 ½¾ÈH¤¥*œv™YÊóäטû¹’[F=X˜t¼^›¹Ù¼b²@,¾;—™AðþzŸéžê
Ê!§k ³È=Œ×äª!ó±ß"½6ü6X IŠµ •_5]Á}¦ÑÕ/õ<¹È
Eîª"ÚÄk!o¥¬¨I°KÊûWÅ<57>àÞæ`VØbö
̽>K\âÅ°Þ!_ò9ni& {5èJÔÀGvsÕÖ¢î-w2¨ñkººo}£Öò°ùœI¼ô×H}05Ç>ÂÓÎÍPN× üµåhkÝsø #2¡d_«îº$ý& Œþ;ƒÝlˆ¬3¢@¸©<EFBFBD>Ãس7¯ÜŠÞ´øÉOZ³ãI ÔbºÕKÛlFú&Žås61}àçÀèŒÏõäÀ°. ZX0w$À¥ºF˜ôö©UW¯&TWÚ<EFBFBD>` 0³<EFBFBD>:냼Ž¸-[¥Ÿ¥sÇš%æöÒá0¿RïWì\Ž"£sìú8¦:û5  î!='-0'ì6<?6;ßâe§…ýU<C3BD>Ù(œ‘*qâsÁƒ6AÛ³â<l©(Ò0 wjü¿^PX¼€¿ûÒÜ¡jAW?êƒò<C692>l<EFBFBD>zC
cj.5­'˜{ «oe14™˜<77>…lÂN2£<32>¨7|ƒu°Ç„€$™2Ë32íã$óh*­ ÃðÃÙº•À3oû~ë°â—ó$}¢Mé QôÍ;A<>\1wjÝîqæ(ÑŠ7ð¢xÑ([_ð¼sÙÓ<C399>‰Ös~Ow­œ &… Q<EFBFBD>»ÁÉ¿$•@õm°¿¸ƒßA±G‡k£|ÍFùîùnÎ##>ÿ•€hõ­ Ñx4¼ ô[%ßV;ɶnn‰Ê„z}¸Ù€) Ÿ~ò6N¤ƒ¬€MÓ#¤÷- ¨)ÙÎíT}+ÄœEÝøyÏ&¶*"+!ÅîdòËŠlDUIãÕ'»àc/.´O@Ib$ Šá\ônwœ$£'/ÈãW=à„ˆn—ž¦v×µÄañÙ¨²¸<A=A±GúÛ,ßjýañ½Å{ÐQѼ<>r,ï˜ù:~y…Ar©ÇãnOÂM3Kqÿ\ÿ´Çÿ4Ÿ#Þ+Äß*<2A>w{¡†ŠPš •™wŸC†Ç éÑ@ºÑÀå çT—¯e"ßI>î~&ò)¾ú„?eðò9°z&1cæ7l3ì5PŠf½KTU5ÒDÚr n|»<>¹îíŒÉm69”—»ºã¨KÊ­ÞåW^%Ü -S«'£2jW-2˜5.<EFBFBD>z5ûÙ @<EFBFBD>Ê¿ê¹$7žXT[½´ˆL[ƒ||Èmni÷;[TúϪoIVÆ_P%È©i9ÜØZ[ÂÄòØzn9°g+oXÞïOûR÷Ë«Ó é¢»8!RYóbò£!`VIk
ƒ(aÆp`ÓbÀfÕoáµ¼.nâÓWŸw½î¦{Áþ~ÂëÚœœÖpLho/ç, K.ù çe`ìíA<EFBFBD>Š± ;¦|aˆ«¡Ý˃Ï^Í]<EFBFBD>µpFî Ý»îÄC¯!6P³·`e$B<õ³õŠ·ë)ËzÈWþ Û-[<EFBFBD>-Q UÂØ!*K-ž3Y®ö?f8µT­ sî<EFBFBD>4w&6ÓÎáhÈäF.ÚúϯÎìœ"Ü䣻2ó9ÀI‰¯gË„YËñ÷:Ù…+:¹<>ó8 2ÒÖõoOª²³Ž+zqU³8jÉ`aÅó3O—“Õå´rÀ¯ÆS²{ ; Ð<ÓÊ<C393>HÛ+zï÷Ó*د;ÒkÂáçW¦9Ú<39> ]àÄàËŽµ wSõÜ&”Œ7™L:UÁ]ß&1SХѻoau5êysiZ š·x½uÄfQº¯ªµ‘¦8nçïå‰Z]øÄÆû Ÿõا[iZÊa+^/¤A8ÿqá(%4QØBŒg(7ëŸB€çˆ<E2809A>Ê7~Òyœ=h¦´Ôn0“¸<E2809C>«"šÁA<EFBFBD>à.^ù¯í6]µÇZ;öÆøxÝO0×n"üG4ø¹õg'b±ßØýà¡Œ™@bMcPÕ ƒ¦P\~V`\#pŽK ñ°¨><>¦ ö—DüÅ*ÔÁbû®XÉ©Êxw?)´CõfÝõx™<43>´ÔŸüŠ¢ ºØ6´³[Ø”¶¬^gIÈ|á½r%<25>s—VœŸl¯pÏgµ2îÏHÄ §0@¡Œ¢¬hN®Ÿ¶t™Ó;+…ÃKý™çèÂß@'ÇtYÒx PôEêWÓ7x½cë´ßWA]B FÕsƒÚ¿8yj~îÐSÌ9½£ž ÃîÔ´÷"Ø£·®íÁÐsQêWêebV<EFBFBD>ÝyåÐFtµ¸
kæ4°º_¹|UX2ÅlzËD ½¿êf"†5<EFBFBD> œº|ŽßÃÄŸ¦”ÃË¢¥!I¬ÙÙ»ºç¸&)AcƒqvåØa 6é<36><02>4µÿÙÛÁˆåaÍ/,3!PX©«ê<C2AB>KÍ¿µwÍP¦U˜aA~ŠësŒÖæ…ž/Š œYÑå¤r®Ã°5-ÆÌRFÅg<C385>¢ý`ŒÏ´ÝäækcüŽ¿²Õ0“¿Çž–/5Ì,2?@G5dl—u]tsZóv•à3n´ q9‰“臠°¿f¡Ð¯ðY¹¿Ì!ÀÙ*ÈHÍP%Øpñ”¤â%íË3CtÐœnT%?û*x|5á(È®îEI‰@ ~­Ð<C2AD>Þ~Öt«`â|®,ÿ<> (º‡„êt ŸÂ,8å³(0óæÝãëtsê,![”
zþc};ÊèY-ŸÃÚþCVeøø)3
¨Ë}±Êø×PÔžú~lše³B¥³«wóËÇÚÓ_pc5N¢; 鱺ÉŤ<(ú`ñg²ô·¡{ jóîr$0Å7l þ³3ml?­Õ<EFBFBD>À=!×¼?G+[œi2j2œc. ÑÚn-~ȱ_w>ɽwWüRË3¿¤ª¼ž¼>@@ͭ亼pQÂyðz³zÀ¢ÌFXbòª*<EFBFBD><EFBFBD>¸y«J#<EFBFBD>_¨DgI0pì^ZcrÕâi¸ ÐŒ5Áa i<EFBFBD>ä9Ï ˆÊo^ä+B8ðó:Q÷ì´ß· nzp&øÇB<EFBFBD>ÐD¢˜¸(0Lñõ\l¢ZËDPÍhb$e<EFBFBD>¥r¤¬óÞàkìïz¥/ ¢§¼ñ$ž<EFBFBD>îåóáâa«â ·<EFBFBD>ÈFZéGó¡¥XIÌ}a>kÜè_RFd¯tU`0af êllçíþçZ£Ê7\òv-m½±ÉïW7ÜÁ|"ípªMá‚å楩³)üf !AÄ®…<C2AE>ÿ©T'u_ÏÊ°ñ¸´F¦?T\Yx9e?«`ÀLëüÄy•ÏΡ.¤Þ]µ>Œ¥ÒÏÈÇËT½šÅçß¹oTÛfdã"jôW¨õå%«}K§4tšþ¢-Ô)=\$µAMiRNti¿ f´uâñú<EFBFBD>h}<EFBFBD>×ñÛä÷|˜óöu$ÿ._@
Öì¿ßåŠK6û×Y«ó<EFBFBD>éÓ˜ô~@9~' »©A7Yó£ªÎ nÊW—½!Ådu©go†edïÜtH<01>sLÑèíΡ"rû-@]ËÀÅ·ƒÍ 7Œî ÷XEí#ê=FWoó8'O?35$Ÿf˜FNz4H:ƲóÄ rêÕ­z ÓrþØ Öûûû%ñ!ò®<EFBFBD>W<øʽ}Yf8½S>VÕôÏÞã 9öeJÚ8ù<EFBFBD>z²T4Œ¡ÍËá(ç@u<ETPÑU/dz 5D³gàÑq6§<õǨž ŽF«šz±Ð;:î±»ÛÂ<EFBFBD>ÑÇÄKñkjÞÆ05n+-5nqIÅã7`¹x<EFBFBD>1\ö78<EFBFBD>ŸÎ3U¼´è« ˆT ¹vÝj¹É1-;?È<EFBFBD>˜§Ñ¦£ž '™ÔAöEå=ÆÂ<C386>RÅñ° Æ*ÆQßüÉtbC‰´ õã/îÕDp<y´<7š\íâO||¾UMGisóz…Ðø“z"¼D&ÉaŸ¬Và°kLY>{>ÈdÄÊײÅ|Ìç=:»æ_šAåÓ`zù<âJZç³…k<VA<>“Lø?)‰§°KÒÀeþuöm¬Â|Ôy†¯šÉÉ<ð­ÆþMZªœÐše@é`ãí§<> ;†w‰¾^‚¦Ó´[px„X»:áɉ5<E280B0>Prû¤ö‰<C3B6>E(
ªšn0IixqüšúŠæíè>Ñåæz÷s¬¬<EFBFBD>èÏ6¨óè*)`TVº` ñðw(7O¬} Z#?|yX,;á y6ÛÌ ò¹"Ef±V2—GU€°šã q<>¡<EFBFBD><C2A1>#/ê<02>ĦŒjÒ⤡o; ³>|÷ÜÑAH#Éß/sìYtW÷×ð*0F¡-"U1q$qk7mo0ŠöçLÛƒ˜Á˜¿M \f¼øc§è/îyOÒ«[ õ£·1Œûëý±ËÆ¿(kvÞeÞ;<EFBFBD>Ʋd|ïY
þ
ŽêDR<EFBFBD>ÃM<EFBFBD>Ühj5Dì®L¹\U>¤µÍ»Ð%!ZÌšŸsˆ<EFBFBD><EFBFBD>?ñ |ÐWI¨û]Kr$<¯ÅÌ ÑÇ0U×Üf¡Õp|Cx.»¡<EFBFBD>¨§û1Áüؤ/ŒÏ¦f¸>jÉH£5bex°¿zv(9mÆR"=»X<02>Ñ»™ôN Ì„­[kÑÃãŸä!ÉÓTÒüWè3"¿ºpˆkpõ<ˆYK
<EFBFBD>õx,¹-_27È=¨ŒX.A7^žBg§Kp®3<EFBFBD>É[nZÔUý<EFBFBD>˳<EFBFBD>4:˜ A®ñ½ì<EFBFBD>˜ÈCA<_;í<EFBFBD>Of¤©?kí!0Í
²Ã[{¹0"æ<-˜ývVúâTWY€^7èLZ®~cg•n÷H<C3B7>E[o¤­µÃ<C2B5>Þ±¤?™·ê#>)ô´ÅIiÍTÓ_Ú-NÔê±°¦WN±*…l„,±§†ì<E280A0>¾¿ŽnÞ}3£êÕèaN~orîïÓú~ê„ õ•¡W‰iØ׫ν÷8¨tÍÄÏñýì/íõ /#i…qÓ9óbÃu: Ô$ÖaZ%ÁG,“ºͦBôOˆ ž¾-doÅ Ì®%<>þ¦à+£~™®‡˜3ÒUoE¼šÑóÃKÑÉTÃîøµ
jgæä©ó#õóÐøÜm Í2+Ì@úO0ùdÄÅzÌgö|ñs:³0ÍìêЯ͌ºf1ÓßÅ¡^E´à¬é$¬§
Ì=üÈkÐÑïÍ-¢£þßj®¢¬ò'É¢‡éwnhk%¨š* „Z?E)t9TÞêZƒ l˜s[Z{ꕱjÔG“¡<E2809C>ÊšÙ"¨ +šÅ ä¹°¤! óSFY«å‡ÎäRÈ M4Ôýu½qBƒKOÑJW×5YØ5óØ”4#c§omÔ£r{^çÄU<C384>³µ¡@:¯ó±<C3B3>åð<E2809A>û|$¨?@Nöcïã¯Ýæ…|¯CèrÛÝž»þÇPDyröf†_á_Û3<C39B>£&9[ 5zÓ=Ñ==€¶KDÙ@¯ÕžãÐx®uˆ<75>´‰¬kü‰%à“Ê”§\ë,,ñ°ˆÔ=Èëã»4ëÐ{?<3F>¿í¨Eø%t×Ì}¢<>^u<³6G{9AX /™7·¼Lü>ÉkNk´ªÈËÀù<C380>x˜7üû®@&Å]3ØV!¯ášG¥Œá Wâ|ÎdóÇΰ£EàóõååªôXù—&bT?’Ç §7ŸÆ2-*Í\˜Š:<3A>HpDQOœå„ÿIBNàh³Ï£”×ÒÁé&¢ÆoœÏ"Ý#mö²ÀvÞ>þÿžÆý
ð<H -òjkŠò¸ Ì}ËÅõ<EFBFBD>t«vh9þ2¸7°9á£çS3µ˜°o¹nÝÂ8#Ú£{¹§T<êèöÆËÌPGŽw>§Êë6Ûw4Xó"{r$(©5ÏpGñåQ3h…,oê!Fæa<—q<E28094>ï¶Bc®So£è»©WÁçhgŒº·ÆMVž#„ˆe<CB86>ñzEüÎ?ÊÈ/þ«6‰äWØ“¦3LÈÒþ˸M\äï†øÝz<ÏSìà 3á×&~•5§iµÀCpóK ie^]Þ¤Ëù;Ã{ò´—Š…„…ÉüªCƳDoß«'jfðU¼i\ÈÊaíÎ:ÕdŒßåýóé…+Ÿ&5[Z lKÆÕã“Zóu>¬fÀÓg<C393>©ÁÒ娵Ø"<EFBFBD>Rmfˆý.w!<EFBFBD>tÙŽ#ûcäŠê¸¿ÎPÈÈ°\Ò*ã 9û¤íµ0³É ®@')3ÏZ{¿†­8|¦ðTV`ü@‡¨ì¶Q[ó]gwóúò¶<05>Ä€Þž­Š¦vѳ‰Ä”åKÛë±u[ÆÁH°Ëí<™I)YÆða ýz-•@Ö<> Xk*ÄfŸì£ê¡²kx2¼À[ÙBK¬XVö¹žÊdEUiŒÃQ.ûÒ-ýC³Éîrmœµùh*ѳÔJ)\Ý…pKî;´þì½#E!éh<C3A9>Ýÿ†!÷_¢ 1{¡ÒÌÛ"¬ŒÌ?ÍcwÁ~3²ÌU<C38C>RÊÍÛuyôºÝ 8F*®àP˜˜„ÅfÚÌxòÈ:»`ÈìÒ+±
ªBðãøƒ£ïDŽˆ0?F
 Ð+AaK$:añE¼ÔÓCÓìr.ë³}Þ¥u¬¦ÂSøø<EFBFBD>Žt¤yž?áÒÜË%4ù3}?1»,+iÅá¤^ë©©²l·<EFBFBD>¾žC ì§ôÓza )s¸U¬nWÀJ÷_;Ûwà MénVw˜ðƒ¥çÓÄOªJ´pÙP³3_¬ñ½GN¸_¯e¢Õ0Ø|ãÀéØbûç°' ý/[IÄw 5ƒBD¸Ì‰dSG>X|õŸWïâ <20>Ä£Kžˈ„eŽ?‹“´ž.¯¤R&+<2B>3ýõñ>ãÙDsJ=äêeó`%-;Ôqf “P®&3"6iDé»É|CV9ç8]¹nçšajWs³!=jfîé†mu L©º&˜€¶‘ñ¥˜*C­qÿ|ÜóĜҹñßíAÛ_ÿCgHÑYÄi/î‰è/²ÅØd}]Y+ß- £Ekð»$ÎT3ÄÙ_8 <20>ØÈð<C388>P&Cü©ÊÉð~[¨¿eÞ” /GŸ|ÉúÂÉèç6tàÇs±¸2TuØÓè_ýÎî<C38E>ñ*ã)ö£;E„9å…=Ä1[*s˜£jq€Jir~…WóT÷S>9ú6Ñ—0qÖ{œ ®é¸Â~ZÄÎÞZa±š“t¢Fôºk11Bwý‡ÆK×!õEráyÍ%4¤u-;1l7GdYeBNØ¥T-{Åâ~ë‡ï²•¢î˜áP¦š¡ô”VO»Ã浘»ò](OÚV]Uó<ßåõ‰6”ÿö3tÎ0Ù>W¼n[<5B>\l2öÜGBÉ1Ln½‰ªÚ¥¸hmjS<6A>-ObVô©”ì<E2809D>Æ3ƒ5³Ï§•½äô©BÌÏG¯Í.‰ áí÷<03>õÿ-¨û5ç¬2 íç–[iŠ!ÒöˆSF€\ó…u‡GSÒdøVãû:åßjX€§w…<77>b<(Ìqš·VbPwkØLYÓ½Ú}Ê0RñúöÓÙ-Z£gqGÂC =0,‡1$`ʬ;&8³QÙ³\VÑФÂeÈiP¶ÃîŸíµãª â/Â¥ë‰Ñ´3zO™[<5B>ÁÑâLÀÎ\ÔækÃOéøZ[WalkAv<41>Ù |t
Ÿè#ð5[tùLU%wUHCBCÅj]t±fipi¦uˆ³ö4ƒ»¨ýÉ©œãsŠKÃñ´`jp¢ýÌ{DåÚD±¹Äæ@Š3 ü÷ߟH!S6)$ñ<EFBFBD>+#Ø^ D;ÕaëHÎñP~j]èx¤ÇG;ªÿµðϴʦ«yæñõ(¿ ô£äŸ;c¨ÒãTïjo¦ö¸Ã<EFBFBD>î<EFBFBD>¥&qi7>è¨1<Û>B§qkhLŽÖ´ uŸÀÏÊ<EFBFBD>4ÁÞZg<EFBFBD>¼½µÅ¾ãiT¬EG׿@1ÍËêð7ë>iÞáAåD*üÄydþa_²ö\øAH06®Æy2½µøw@È-Tv¹<EFBFBD>> vZËY_ÁIÍÂÒÆ©ýÇ9¿«Â%Š¬pÍÎ)ôð"LE¤¤Öò* )NQÚo=ß¹è•!±ø¤ºüñ)4Ùx[·ë®»°ÜGG=ÈÂUzk Ýlû|~¥×–>¿CÅ`ï¨
Z6¡C÷ý³O½àº!ÞùŒKêEÕØäÍêéóËïW/Â<EFBFBD> »³)¬Ô#Z/d>ê~.ù<EFBFBD>^COöS°êQÒkh<EFBFBD>d¥þxm2­.Û%77¢ÒËšFŸyÖM<ôV ²LVeÁÔý5¾pÚíJõabOÈ<EFBFBD> þÈpªNŸ4ÿ>9òt¦nfšú;Õ¤ƒ\ª¹X[¨ß Ÿˆ½´µ<ПŠ8K#Êà­À×úÀ¤-§ˆ£çqN÷7ä»þR}ò,C$Æm²>{^/b7²ó+Ö-JaJ.·Œ1-q{h}|ðñù ·q|$Ìy÷¡<EFBFBD>vq°Õ¸¹Y£,¦¢æð]·Ùe#-ønüRD×:ƒœ«C{¤¹C¢dôaÊ54ïÜnê¾uUQ~èRà#Ö#¸·}Àz]-íV¿uÍ)ÙI¸Þ ˆ»375RDÍXK6TmŠÉ<EFBFBD>ÌÒAD8NO#ŸQ+÷Ï,<¦¼Ä`øˆˆÎœ°{pwÄ;È<EFBFBD><EFBFBD>¾¾Ãµö =¬ª,žÏD|\iÝ7M¿p´arðO¿ÔüÈ&+QcT¹<EFBFBD>9õÊV9X.ªÜ1Óc^5ÞJÃÑ¢¥Ãµf_¬ )¡î£.Ht7ñ+œD<ÏÞK&¦ê2¤Kº\XKËÕÓ à7þùÍUŽP=ɵñ饊<EFBFBD>ýœía"ñ:ÉËc<10>q>ák¤ey˜<10>ëÞìj]§2l_§Jïw]µŸ¦@y!î¸<C3AE>w©P­Ê§N>;É÷ÂD`8Úül>àÒ²T@s¹~wƒ—æÞæø÷Ü#Á
RüðrC`DÅq~RAfÅÛ ç|VèUÖz[ñ<EFBFBD>¤÷œÖêD˜Yµƒ
<EFBFBD>ð¯%~³«Òò7ó´ròõ+Wuqú¤Þ¾íeo¬vùJ î: "à¢F3pdú5ò{D´Dy‰¬˜Šø¯+aåŒ[@x <06><>Ûµˆâ?—ðq!aÚÆï ·|õ¡ƒK|SŠÝdT¨ŠègóýêÑñð@pTˆ Mã+$6DHb?ñùWœÇ†M“Vߘ˥㕲G÷¢R:$Û)#ó¥#](©±|¯ûA9“Pë!k ÷ê®98%îÿú@dgð…S—oZ„<5A>S{(Ô_ÆO=¾AŠ\
¸É*ŠŠËs3 ÃnþwšãÍ»ÂÒ}žåÄ+£½½BÜ5ÍÉ
cÝÒzaùôùbñÁûQa иD"#aŠj r%Åè<Ãô_UMhN>œ<QZ[LrØ#úÒµŠ[°)¼F…5®Ô:¾áÖKœÇ´U-ßÒl×þñXàŒÚžBšš”ÛÒ5úŸ9C
Ê(;Œ<EFBFBD>ÿ-qÌR\=0DµKpÖy5b]:¬<EFBFBD>}|}ÿÁ¤#Eʵ©ªì>/ÀÎÁvÇ8JS´gÞë)<EFBFBD>èýmû
$ <EFBFBD>qe1ÿQvfF9434x[NŠ³ºåߟïå kv<EFBFBD>x¾Jží<EFBFBD>+W8l£q¥Ö HãìèïN¸ËBh<EFBFBD>`çîO

l(ÿAW° WY>?·îÒòm£BÌZWJ,^SæD@ëgærRyú99{VÕ¢Ìm²§çž´<EFBFBD>´ÃÛ­ô"¶xÉWAæÊ\ë,ßn¬wálë¼kNtw"m F¹!ruÕßy¥%Ë%4>¡ÆøÉÖ{0%íÿguwƒ¦ ID Íý¬­Ôo_k¿7¸=ÐÊNŽQä8®Ý¯ãö<EFBFBD>žöÆû#Ïð¸ ("Zè=~ñ%éU²<͹[&EŸþ Íj¹à¤mÁ†Æyÿ<79>:dACÊL•o…õøɹ‰\Ìž;S‡ÉhSL` öá«á'û>7` ëðA-ÝÚÝÎS¶¥u$jeÁ<04>^ ¬a¶²XýB;#ZPb „ŸŠä0Lÿ¤=w•"ü¼¨ž/dÿ~?î7çÔ4Õ`¹Þ1ÿ";ÙAâjÐ9,3é~î¥OZS´zýH?Emê“šCÝŽ<C39D>Ÿ ç„?<3F>U}_•®h<C2AE>‡±´D÷¥ÂÐÈ¡ìq;ÿ9šþ¤W8Sjå«4Ö0òÏt; å¨{¿!I2KÁ*_ ¬¼g=>"Jži"<EFBFBD>¾š²åkÆÈR£Žð„1ëÓ´˜Š7ˆ»>³©AÛŽÐÎöú©Ýœ²¥æ㎡Ç9¹†ƒÕ<C692>|ÌŒ² Ô'~ò-ñsðƒR ÀÌFŠÉôkqÅ°;düpˆ@šÏ!<`t"gÖGÖB!à¾>±5·¿õ`VmŽ«Ý[Ò2sšåýWŒQS<EFBFBD>@Èî*À}Ê=D½¿3ò%wpYfŒ'Uºó~Ÿl„…‡zHKµºµ_˜J¨²fùÕÑw—aÑíôŒãhdÁèW<C3A8>1R…}ËœÂn •©&zŒ*p)ü4ÐxÀL™<4C>ehàødw}¦§hñ%ˆ¿X‰Ž¶mŸ—öXñÆ<C3B1>R̵w†þÃð Ó ötOâ´ ÿúÿý_ÅMš;ØÙJ-GÃ7Xa°¥?…ºÿŽÃe %ErV®œ¢“`h<1C>"4¹ªð<C2AA>¾ñ¿«IÜë)6Ì\OU±”Í8H`É:™´ç4Æd÷‡ª¯Ä<C2AF>}#\\n%è+n`!AÒ¿$‹šÞˆêcüð2úp̪ƒSŽFÂ7<E280BA>Ÿfä:K p±WдùÝÌšv`<60>L=C|t*4KüµC jÜEœÎ +ÒžHª#h`Ü1D1 2ÕD§©TÑñP>œbâÒªeŸ=º—¤J{ 8 ŠF øÇyÁ”BêIv̵Oÿ"¿¾ïµOAúã¼ißµ2Âó˜°„{ñ«|:gÅ^åÍ1«Î<Ôn»ÅD$rZ_ñ…†uËW`|‰&þp8E¿!š` ZÅÊd¸²@± ¸N,bÏñØ&ÿ+äSém_ø7§þ™%܇´œœ{=nËP·1÷?Ê‘´'SуcРivÄë°¸[´˜O¨IDSʤÄÕ<¹*@Ý® <EFBFBD>¼<EFBFBD>#<EFBFBD>Ý ¾ì%eëñOºó^.á§ÑúSEØë1]$&8d¤)îí¸±v$ _-'·]lRÄà-åyw-¼~|“™JÒªÅá£Ä맳­còÄ·R¬ÊK8ÎÓàs×ÿíæ/zïõ>z„ÐY&|˜8N977>Y<>sb¸]Užé>n—‰h¥»Hé ¤’^ß<>S°¤³ó<C2B3>×l¶½+ï"yq MÇßÞLC×aNîÿíù@Jé:hG}]£¦ûHÛ“'i
û
ð=ÆÉNVGã¢@WªqbGÅ®r·;GB<EFBFBD>MËæÔú<EFBFBD>)ŸªMYNT(ÛýA³ÆÕß3PÚT.Ýå¨ËŽt=<EFBFBD>^Žò·k<EFBFBD><EFBFBD>z¾b.ok\CoõÐBN$jl$ Óô˜´@˜óË[¢ Ç"v²)¿É¢'ŠÕIy2ÀÅ“×Òï»ÛŒGÜ1m j‰·ìúïŒÍðò"@¼Ë1ÊJîð7ÞW@^E^¾óÙC<EFBFBD>¤§~¥QÄ·oK ;á0ã3!¡KRx½Ó8S©ËË­MMÜ$<EFBFBD>ŸúO±%ÍP9ÿV¿0V>©&.ôÚ­×j©<EFBFBD>ÒW´aÆe ìØ#V|£d±Ü¢˜Æd¥úà#¨ÁÓä·Ùn ¦Ep9ûƒÜR¼>«B<EFBFBD>yqì ÍH,»9Îl¥ ˆ´Žvnѱ|<EFBFBD>ˆÒõ<EFBFBD>cmYÅé¸âŽñîê5a¡{À¾:÷*¹äü !ÜC8¦æó:ìwêÛGÓAÄs(ÜOêð°] ü{§<EFBFBD>÷iaÄ ¤9ÎFðd3eÖöÅè<EFBFBD>pý§¢ æî±y(*?làwƒÔÇø|'·ËJ<EFBFBD>Ó?Üðk áhžwAžH£4<C2A3><34>„*6¡8™ª,4ü2\i˜¢òæ­}“Ìá:­Ú‡0·ÀAuà<C3A0>ТW´O¸Š§ˆkzñË£üb¬¦ºAÒ°ÔõŠ¿ð‡,5¢f¿éBÅ"º_hºe0u;DqÐG²R¾·^Ç\1hwEwâò™¦¢<EFBFBD><EFBFBD>˜1·©àu³½£c߯mÂqƒ°âÂg¾ áÊæ>æí¼2~õ€õÎvg5o™óòâ7ÁèǺ±7i<]ªˆu<E28093>¿X ˆ]þv”W -à€üäübcP˜´¼L™ä¤ü<$­ìƒûÍWrÚÂÔ¼àk³“, êÏÑ¥
o<EFBFBD>÷Kl<EFBFBD>ú~m8è|)Çnd3<EFBFBD>6œMÓóÅYy{À§x
â-mÅñÒ{ Í@·äÄàÎužš<EFBFBD>µËcÏP'ϦzáOìÐ)¦Õb/ç)Q0Á$[I¡Y—WÊANéÇšv%D²!LÚÒ'=ýqòÉ`ƒ»ÂÃÉÂ/449ÒÐ|ˆj˜%òªìYÌ7ä²ðTód¹ÏÊu<EFBFBD>œvnÃNÝ{ÿÅóX_{èáWŠ­Ñ$V«67XíRç;íƒ! $¬ºaUSÚÞŽQGCydG<EFBFBD>Ð º¼?<EFBFBD>yàÅ?õãæ¡£´Í¹ªpfßòY+TfB|¤«
Üy9Fš-Ç90D7þgSôm¼Ìphk.έu'Ì©‰°ÅòfÖäHxSʤ\Í…çbˆˆÜ/â×þl¡ãÒÛtú¤Ÿnz˜¤Ò<C2A4>^~|SšU5þD%1j8”*'Æd3Kú8eµţ mwÀy+ŠªšÔhh å ÍÑU<EFBFBD>$áÄo^%Ý4Ãyªÿðµxà©Ô5ãqàt<EFBFBD>ê¥Ï׊ø ¹Õ)ˆR©ô2-,-¯qTGBA÷TaüÝi×@ÛÊä<EFBFBD>ân<EFBFBD>#¨˜òï÷@¦¼F«/>ûV½ùˆÝÉ<EFBFBD>mät<?+<EFBFBD>¨<EFBFBD>: ×
2šõ¿Óæ«"ÙâP§@7V„³DþÍP¾XöÔ?Úº8êCɯr2Ê<32>Õ= Ö$!i¹…¢MçæIWšùÇOY€*•Ø*ig:!ƒ¸=…<><E280A6>}1’³‹Þå@_Z¸²„ñøeâCØLÛá”ÎÁzl%
àìùyþ»®\ýg±˜õðêR+] <EFBFBD>ØÀHþÈDPtšWKñR}H0@M¸XO<EFBFBD>¿#¨"³,/K0v÷@0¡{l²<6C>—ËwZ|PAd«ŠqCoˆrŒ”åë?(¾¼­Â²´é°ñâ†çLÍÉÆñÕ0Ô:aËâöð<ï׋¿ÆÔœ|Üax}^‚µ).4._p[>áèko ÀStÜ#<23>n[¢<>ó¦<C2A6>Ö]¹‘¸Úˆ%‡³—gÆɸµÙƒ¶BÚ.øôôŽÎoö§þ¢T³1 <20>V%¶¿Ä‹ì¶˜,GP]S¯&+M™3@[NsùjZuÁTùáÚå…‰¥ŽšœÒºž-ƒ«·ì£c¤«Âå«0MºÇJæ\OEÚâÏQ¤_<16>ƒëj¯Âý Á/
¿   f2>É£2?˜Þ˜ÅÚßœÍöj»ežÎý·¦Æ7Çfc<EFBFBD>ŸÑæHढÅãï.²ô^ý£ÙTM êpÂAò5Ñ4ïü)¨Ú<EFBFBD>Ùj¸8ç½IÁ<EFBFBD>D Mÿ@²ƒõhDgkŽ2ú<EFBFBD>$¹Ÿ¦¿Ë´¯É_{ü¦Ã9Q²´æîMö³màè¼d ¡F+Çà<2#!Y.³ÑF?\";GÏAZ¤jÊq"y>Ú6/KWÈxñ5g)ëÑíÁúÍœ'rÒ§³<EFBFBD>°ÕôÈ ÃýÌWùë"×Ùéój,Mê{8œˆ„QA
dÿQ ;3CGb³FH)Rmcl©g¡2ÉNúm~Î~§<EFBFBD>|Ùí|ºøÒ¯&öÎbñå81õ'㣟õ­O¢rô|é^}Ï/W<>Ì\ú2®dÎÑë&6ùó°ÊÑ?ä/È@p·4/aK¨¼ËhUÃf~Š¢°PÈ—ÙŸ” b5©iñ±6Ëà Æ»œø×èZ§Qƒ5D`Ö'÷Ð¥:#"Ù8¢”|H>üX¶H ¼Ó‰ÞyáÝßwgþéÑÞ…ãÝåfŒ%øt#)°5ÀÜm¶&ü?wºÕbìÑM[×hnõZ\†~º“éZqW, •díÌ@ô89aE,ÑwÛa_™qô¢c dêÍW<EFBFBD>ûB¹uµÓŸ¯[[/egÔ•²b²û</ÙX`ŠPµ¶Ù ÿæ1i\Zr&
PE¿šG<EFBFBD>ðð)?¹U@­]É?Š^BÿÈ ˆ^2
³|qþöŒ½ª~¨4U6OÆ¿êq<EFBFBD>Š@ê;Äx¼×Êïu¦Z¡ÿëix÷ìyqÇïððèÿF¥¦LO9^lDyAèLt<Z°ãgÄoœË²<EFBFBD>,%6½<EFBFBD>ó)ÑšÅYÿ¯<4ÊŠ²G³È³«Â hiü!/<«I7ÿs¦*ZR(5Ù¡6§š8®ÅVÛT²Î<EFBFBD>s1âK L\Ðb¡§ßÒÐèœÆH*<EFBFBD>4×<EFBFBD>û®gN¢0 °[j?ÑÅÆN/ÏzrímóÚïõû½°àqC
K<àBÞÌ#é6p°ÕXjîWNšÅd§]ÚA]Á¾urÃíê Ž<EFBFBD>ã&<EFBFBD>¾¦h[>±ÎÑ®®X®¹ÚE~Ø ·dQê<EFBFBD>Vø@îíý¡</tzT:¥Þ­¯2=­^PZ<EFBFBD>§Y\{obaX5J­ÜÜ÷±ÐJ¿Z¥¹vÜÊ=:mTLÂÒFØ<ïÝ®d¹û-Ž%I Úä<EFBFBD>Ry¿Ñ<EFBFBD>-4oAß>J¼].ËGtDk'£]¨À'5OšËŸNštœÙ°¦æDB¿ô.ÞßÙfÇ8GaY °h~sê*l³ ¬0 !1¥7?<EFBFBD>\fÇiwCªn{ñ$4~jçã÷£úrÿøŒÚ´íw#D[®¢hßÀ2µÄçèÁÌ
Túf øÏì p6]<z¾º.S14íšgCÃÎI±X|{Hßnbµ®r¤ÁÞÑÝ+ k}B]â¨RÖµ5Ö'Ù^¿Þ½â¥èÊŽ^E<12>,o§ ãìl¿,l‰_#db,´'P))<EFBFBD> Mv(<EFBFBD>.rQ¸Xáp˜ÍûcÁE˜}f©Å
û0üŸN»Ÿ|œš§$ 4Œi·ÂÂÁ%ÄÙÌÛ__Ðथ2ºå#*ñ{wk`F·0q¬U¨B>>í6û6]@T¦aRË1l§¡í'¦Ç¹Ì‹¹åÊ$Mˆ­<CB86>1<E28098>—+þ·,È@…•·îyú€á#jkNèYܸëèS£Ð÷6<03>À "4)ø)õ…ÆM¼ Z5>¾¡aˆkÏó¤À¤ÝD3¸É!O»öåœÉbrâŶñèÇtpêÆ~ Ø€ÅhPÞj|
Dn´"‰(æ©´É<7F>r……<E280A6>MÍÈ<C38D><ƒ¶I«Ê<C2AB>LqïJÈm³Na#s#ûÎÜ{rh<72>â0ùض°„Ër ¤é¯xž‹ÙåŸÕœŠ´2¿n^üN&°ú)sü†5DT„Ó5M0Dw4%žâ®v;¾Îd¿tEÊ_tÕäáìKoÀúãv7[ÁÔÃYoQnËš%§ .<2E>ùw•¸òÁLÎÇ<EUÝûåÿ¸ÔKKðÿüÀÜù  ¼ d¹<>ýQõy€R‰¹je íRèWj®§ ¬°n.«P¸<50>QKÉÝ°#%±Á‚Ý[ÊÀ±×½ÀÀçžÙVwGï‡9cUòÍ]ÿ<>4.é67|\Ö `¤2j ™RDýâ¸ÄQÑuZtÊä fÃï²×:¶N»œ Ó|<7C><$¡à´£ÈeÅQ¼!ÄÈRÕ¾¢R×óØú<çú;€õöcŒ\;"41jêÝòb\œZòcÒ.b^Ÿ<EFBFBD>4mxšãÃŒ ê/ñ®ï4Ëœ¨¸,¾Æ=Û¤]åL×V}Øêâ§ño#[D <EFBFBD>Q4'"3<>'6±
?:9bøj¿ÑÚ;î\Õ(Æ$z3]ep¨Ø»®5oV3ðÓÆÁÀÁCèƒ[©ld®Eô& ¯tƒy!9¿0Dgvj:+ |={üUÖOöZŒa»h¨>cw\ÆüäÜK''üd1œÑˆš7<EFBFBD>¸šÆÈ"bþ»?Qmä^Xxzï?)Üvw'Òd*%óØbª9É~Ák†IC•çj©ˆð8«u“>j°âmC2¦}V†¡”¹?òãz<C3A3>&#J²°ò´™ÆúÓF ;ÕÿŽ«õ;Ú¾n /@ý^"*¨ªO.Ëö¬^©c­SÊâéLά,ë} ×kqů Ôg÷Ug¾<EFBFBD>Õ&`Šn¥h?ÝQ¸<EFBFBD>Â*{«WèÛÖÕÖXýðëêëm/ÿB×e*b9,'$Ÿ°Ö—4ä\êUù ;O­e]¬`¥ž<>)¬_„<5F>@3‰ŠLÒŽh裡¢$æɼ·¶·ùüÅ<C3BC> 7ƒ30cœ"Ù jÐ<10>·c}3CQA
3ž%¼§HM<EFBFBD>OLS«Æ/Š=|¥d#j˜¢{T¢îoˆ,¤<EFBFBD>¢8Ëëº@?ÄM¸Ò/
\Ûy ^¥±ã·ï z¹07E$ 묬<P>£ÎƒÊšE
ße2´[eŽp£Vn®yÿ<EFBFBD>Ò®#<EFBFBD> Ôâ«X<EFBFBD>Ú;¼¸AÏyl$Ì£ä8¿éî&exµFG¦µŸƒZÆ9MiÓf ¤À­ :S[:_ W\ «Ÿæ§(,¦¯ƒä<EFBFBD>$¯âë<EFBFBD>£4¡.³f»Ê+N¿Ð5 V<EFBFBD>æn°Ò¦ölšd¥š!±¡ß~ã~Ÿ<EFBFBD>n ¢>[²Po_á¢mCHOèW×` svyOÒ¬ôYäÿˆä?.Ú`©¢,øî=Vm¤µ&PkÃVQ¬AàU]!*>=7Q'Ï:À‹÷à€EJW?õ¥a™žV€µnÑdhðåÁkàãV˜¶Y¬úBÇÎå²/vêW^Ë»µ<C2BB>ÓôÜûžñ‡†UÌõ¶ SÛà7†ôÃ"QV“i ‰4[Ä™Jÿ;=4@i^ØK³a^Š¼QJ²0`•¬ˆ--»_øË¡¼MCç³ÅýŸ©ÇÓ°R<¹`À¿Ò&Û·W<C2B7>Ò]Û©TîETzUý{šM¤K) ŒqI6 ,5,1Fµ5À> ·$¦†«bÖ)œ3ßË“ZêsNtÿ}KöÓ$š®ç­ò¶°Vl~¸Ò3ÅåÖ©M<>¢…z¼â¯¥=ós.ãÖTüd'úˆ&Rd_+ýè/9{·Ã8(z~ˆŽ­ôö±\쩘oót f??ÊH<EFBFBD>Óv.¬)+¼Ò2#c÷kgФ&Í)ðà Û>Þ,cÿÑ)úŽy|cþL3oS àªcesk.¡<EFBFBD>mÃOyo[zÁ`!Ì0vb$úN<EFBFBD>ŠÏ´Ðò7»Æߨ©Í¡¾7A^(ÁÏRM:ÆV:.sQKD±?¿BXÇhJhK,hÎÁY29£rn_-dXä@k(·ëž¡¸ °¢·˜µªk¡ÿÔ<EFBFBD>;®tTÈ+î9÷Æ4LS] ûTE@, Ê«V˜t/ž]À¸KImtÆF{kU[3M4þfÎ篧¬úrGŒÜ­îîk¦×˜=?©m|¸ÑDÄXLËÆ
Ë×[H_ßf7` o6x¼rWMhÝ/õˆS.ç:Tkf½<EFBFBD>`kê ¥Úo^cq* 7pñ8ÊK@èå á]Í$£ßðÀÑô)Ä|úmÈ^ ã¬^ð:ï°ÄöC@XüCFµÌK"m$/S>دÆõ°‹ BÙƒ$ ÊL¦“£bšƒ€dz®ã~¶¬‰ <0C>åÙ²aºú9_Bd±§ºP»ÅÈÃýIÖmçXùÜaWçå‡åqsn®¦1ð#vX)Ac”4ù:Òább5iJ§S”wVª÷¢—éi€G<E282AC>y|]Z%  æ¡Ï2Q¡ôï1JÅ×þ&LžÌÔ“tgè I0OQ!w#U¦bTOjŽEG‡&|;C%ùûlQPÙvFD~Q—ïóRda´¯ñ
ÆWÑTí<EFBFBD>hpœn¡ßº¥cNC­?ú;Þ?F]ªç*ó&<EFBFBD>G+jî0$°ÙReGI vw]µÁë·=085ý#Àö/¿Féèpdsjj-£OïŠ<EFBFBD>¢_VZ><EFBFBD>ÅݨuœT®î-ÞÜÞÐÑ×59ÍS´aDÌg÷§Aw<7½%²ª^¢()¢ZEóÌâßEQ]W_ÀUËéfÒzJ;äÊ<EFBFBD>²Çz¦Ò@ûÖ&[ì×TuØÝmƒ9øÎgÏfôó= ÿM0øF=8ƒÇ#]küiZp%þ6œ%³gÿ~A^¢IMÑ øéúaBw*"Ö¼ûÍ„6Ž·*<2A>¸µ´ë⥈ÍPæ{ñÐ ;TÙoa|Weð+B!<21>{9;@
ÈÐMHÁ«(ó'¯¶¿¯pA<2M<32>ɧÿQ4ªÂè(«iWßnŽøA¸¿ïwÕV5!&·ß„;F´<:uÃÌ`m\ÚÛ­*†)îðnÝñÔþîœ#Õœ‚˜›²=%®2;Ëq¤?JYò+š²ìmx oÈîet™}¿Xl<58>9-{ªÜà#§óToêÐèX½ÉÕh¾WïÑ=ÝyÛ ˆ‡*’ól»@±?§¨É-C4ÂL&?U/Š^8•hAªpJ'yS\§e<EFBFBD>n¦AwÝŠ_õu´þ¾˜Çé<EFBFBD>·<âNëí9ô~<EFBFBD>/ÒŒ0vªéfe[ÎL_iÎ<>Lú3ðˆj1ë1ngÂ]ºùÞGPWVtáÝ]¨å<EFBFBD>iSï`{üž^Ž´³B¸ì'çuå™81µm¾þýÅ ò@êi¬`ï†)õÒ[ð¡7H°¯R<º@”ãhÙgÏÐñUk\êtNg}W²†`½<>˜rO¡{(³„ϳ<C38F>BΨiŸ²‡<1D>Ü&æÀ/ï8¸ßd
¼va¦¸­W¾6½a§Û9Œ÷¸­?(ÙµC?ÁeÄ`«]ãíYkn¦'~õmãüÒîðXù`4šƒcÃ̲ÏÒ?ÐÅ7§£rð»YÐF_î5QSþ;Ã{d?ù}<7D>­°¤'ųð[ :nçæs<EFBFBD>Ÿ˜Êàèi.iõx|<šùjpœ»<EFBFBD>¯§«ÓÁÂÈ&*¾Ä ÌM ÜàXdû.áù 9|CHÐOÇO1d/Co²}¿HŒc±k£|{=BgpûTûøtŒVÓZ³÷ž¢1»ÏÃÄÊBÌÌôÖ ¼ÔèWv<EFBFBD>ï¦ê\Í2×~º6ÍŸdiQ
±ºb¾é[šë(ÏØÜ&ù½ƒbŒìk6²#øÁrVŸÌ³
¿ ë¹-÷}UñçÚQE·2ÛÛ:FiûacÕ ³tW:g&ãzÛM$´¨³ôÚ¦`JÄn2Ò÷bB¢±Ÿ¹r9§UJ¥As¡¸4¯OYk*f¯¨ë@ÜÉ¢ÙÇÛ|8<EFBFBD>Q)!ü}ðüéß±Š×Z, áî'A1t¾B†býVÄž<C384>Ë ˜n}æÜìã(~<7E>X+ƒœÉ<C593>àò±ž¾Ÿ¦æûØOCÅÌœQüõ,ÔU¥ùië N¨0%lpíùFÄ»QhÙ=ƒ«£ùÍQ§o÷i; sb¯oÇþ¡ázäåÚöS¸;|æã _3·)SSîÆ?l‰\ÝPù`Ê*.!HbŸ¡H—ó«á
Ýûä/˜@h)s6A·t\±òã¦<EFBFBD>ŽûɦŒ'œ‘Üah ëî´¨’-;«(ą̂¬¬Âãi)îðÌ}ÆpÔ²ž[.óö€¯¥®G ÚÌS01pðÀU<C380>Ú +b½9b<39>xJhŠCÌÖ™æåƒ8ƒtöD± ½DHÂËžŸu¦=<3D><>«ÇÕÍöµ˜¾œÞL@^<5E> ÷*óðt;ìPBÉ ;GÿD(w<>Ecù;< F),Ô17Ô:zTDªTóÔÿ”ñgyæºA³Â.D¨¢­™ªÁ—Ú™üÆT1e¦0.¡À€üGì­â^62EŽÉˆ<04>}uNf-,mòT›»*X6_JÜg<C39C>ý\5Q¹VZ¥¯e‰ËÄìy3¿ÖüAÌÌ\÷ëfÊl·Ð*¢«HÑûZÈ0õŒéùcãViez“ÿ×íO¡Ço«®!¥H½7(Ê<d5<Û“>ÎGjë™ÞäÖÑ]âk¦h(H¼¾óŒŠ=«¦ÁÏ®
zåþYˆà³Á ýñ<EFBFBD>õ&HPè*¬ô òæhˆnkP öˆ }ði<EFBFBD>,VÕ"ëF®©^VwÝž<dÆOõ¦ð%^¢d¤n7ÄC|ÀÖGk¥¿À¤LÞd/½Ìçt9ÙÒ+ÄQGrÍÙÔ‡í-oäiTÊw;;™“j}0ò¯#(î‰Ñ0ô§˜FŠ_7ºX_¥hë <01>s&ŒÌ®<C38C>݈»Ÿ Â¦Óâ¡ÎzÂÚ#Éw¼%àûIZLGS(yìîö
K<EFBFBD>ÝëÂÖ ÿ°<G( âó-ÇŒ¿ÒÛJz9ûªÇ<w¦8ó³
w6¿³k}#£Â8 ŸÕ¼;Ú¤JŠé»s
zãŒÇHtWÒ[àôÞñdwÏ,>Bas;+œ2÷J ?ÔûRÿ¿9ÜýxONŒòK]ÀËnjn%<EFBFBD>e-/
êé =Ò¥aÐÀ¯#Ó}ìµ¼òÇß`´þâš<õy wÿÍÐp®´»jetãk<EFBFBD>\vš9¡aÄGaáióåãŞ<EFBFBD>¦K¤b n¤ȪÝ<EFBFBD>¢_·:D@ÂGò ÌãÓØTüâ:ë/-mÑc¢œ¢ãJe6¸T.}±ñp¾<EFBFBD>˜%Uu»)EÉ8O~6Â%ãÆô+­¬Ýz@ Ïœìê«®ëõÞº½úD ÈÑ0ÆÔVÚÒääjGiv·ÉƒÛQ.Gc ðÚc~£ci¦
<Šv¿MÂÓ/jKºÞdsÿRÊ<EFBFBD>C²Å¥š3ù¯š8ihX·rºJäfÁv)úå)â#qÓâëB:,  'LsŸ…ZÏ’'­$3X<žªPœùOñ5hE߯dWªQìqÊuh¤Æ<EFBFBD>ËêòwªÁIô:ð~*Ò) ¹V±ªå{éq'N-\À×#_¡JÐ"Q(T½ 9&¥6A¹ZI“×b}Ì[
`Õ¢Þn}»}ùñ<EFBFBD>ÃÊI »!Çä»2µâ[ƒÙÞæÎ)uTùäè>od¿MKؘP¬>1ð Ë­ÿš+»¹·Š¼¾jO\¦}]`÷éßíx<EFBFBD>þa<EFBFBD>³[˜ÿø˜Æ\¢²q<EFBFBD>â>ôdÔé˜SÈAð%ø+ÅɸrŽnÈuŒE¨izU@tv"p¡P׸šcæçì©—<06>|ÂÞ(|nñ¾žlYH$ÉýÌ9ä© y>8–éÓYgUÊ;öqeK¶PžBeVt J­âœRP#“&Å5ŸrP¤ò¤`—~“
ÏÉ"¸<EFBFBD>™K÷žwõŽm%õ”’|\¤ûöÃÕ¬;Ó8qáó.ÛEñjîïh~ ûûÿ ÿi”IPD$Èü…«éÌPΩl]<bS½iuÏÕ´€’-§Û €´¶r…¶Í©N7Ž]a©#æÙ¶ÑB÷>¹SYæÖZNx”ºðÛ㦉¦ïú¸Ä †I Jeµ¹ön¶:¥B?ÒÚVá6PgV8o¬¨<C2AC>r´<72>5uDH<44>
Íâê|ÉŽe'Ëà)¾F~üc­p×RßpÂöÉx™ª<E284A2> ßhÜGÉá ÊP"„,n¶…°:e.»X-|ÑKµÐ¹öÁ˜¢øµmrjY{•®H5ÎÂâ¥6ù6+íGÞ-5<·f€úÉà_Ãè834÷i¬Ÿ`uƒT®dQ>„Ð8oCü©ù¥Ô´/BVE<½Â3&… Yó¡ûáI‰øPºí6å:8Ž;ÊVçÓ‚°{ò´±~)ÃøÇSDÅÊ5v¸ÿw¾|fjÛ¾-±ÇI£:‰qD™ùå dÌ(µA±S¶<53>ÜXjM×!\wøºMµö¦Òìýüò›MU«æÜݾ[U³~Ì™ï³<C3AF>¹qÛÛš.|ètò$#¥Ç•Q
<EFBFBD>z~òöw6Ä.°¨ÐQ(ÐEWj:kÕJÕ¡ðjËXaï&Ä®6>¾Óö'0ÆÕë·jÏتï™8G ì·lhÿNvÖu¦oèF¦<46>Ž5Åÿ™d¡üCÇ="xQ(¶+Ø÷ÈVEýº¦…¤Ì)c·¬\,¼¿ò’+ÍæÁÏú£} ýàŒ&S~á87(*»)Õ[àÖÜv¸±ñÛ $A³äœ/5»y­5p¡‰B-¨šµ<C5A1>ž)Ad5]$=áät¯`!U»œ9pÃjøbF.Ê0ŒºEœ‚ã×禓`zûõÈÕ¦¾d¸µëÊX¦öú±ØuææiUê2Š­q‰v ®¢Å@r%µlGFºÀóD2ôN ?¿[ÙÒÙT/}}L†m+<2B>ˆÓY*-Èv˜Í”2¸ öÓä(š¹ª¯ õaÓÂ1êmXš•ô³alƒ~õÄÀ‡»Z[ÑBÛEg>Êx66C†Ü•HvJÔ´cßÊÔ=¬<> îö?¬ðž;¯A9ž<39>ȨXò†]ÝCùAÌú^<[…ä=ÂDµç<C2B5>˜¥„õï ¿/½!a !d÷ª,ša*èD<C3A8>gBä úYwûXoYR¥ð¿·"€[üzÖ!¯Ä¥™àVÂúÿ·áÏòl=ª÷BV†)Lg2®ßç«·?­]QV8‰õ™+&] Í\¤ ^Ê>ì]·<>„UJÊ<4A>è`àTuÉ JrÞî1òâxùi”Ù]œê” Ÿ c‡"¯2…hÚ}kGIÚHdþ_p0ƽijûi…€"0ÓQ¶ëD”…åã]¸R"w¼cigVæÍÁ`"ÙV(š¯LÎuÓœߊPFŽT<%œ½ÀÓ5iÍȺÖ¨EßÐе£Ôìß}R®­­ô™SÁEÀ‰ž<E280B0> E|jŒ+~N¹oikŒÍIîÑÌÀY{ °™Qc©ÉˆÊ“ß>>šŒÇO°³Pb…—¬kÙÔÁF€ …þÕI¯«˜Ú<¾J‡H0KM»¡„v<E2809E>åv\®¹Ê-zjo9š/c9ûô\HŒºã<EFBFBD>ÆÍ]Š…+ŸÁ䰘ϞP&Kõ•ì[1úü¹·ðšgOñ:þ™ ×>íêm#«(n¡~Âã<z†1ŸÎÚßBPf^Ñ(÷²¢à¾ÝH(ˆG2PÝÓ×Kÿžü´s<C2B4>ùÈXÚ<58>ߨ«ä¼_#«àÇJþ?Êü<ÁÚ¾‡ý*Xfy%éë)iî `±x¢Ã‡ŸŒñó­ ¯~1§É 1*ó¹¸CD­š•ÏÂT<C382>^Áa¯æbdw:wÇÇ M”+ùSªÑ(¼V@µöÓè—¢Üv>æaȇA­çv;žØî3@ôÙzÚõ Â×g5osÅHX~ÊDÌ ¦TG:ù³ iíòYtÁ~¥” U¡r ¢^±þî¬8»z¾üÏo ûªë朜oÜdê*’ù=€Vž }‰IŒYñd@²*ß<>ÏBÐíQEY€lºO“—Ì\ÛŸ¶sò÷^ù;w˜u<>ÈÕ ¬±*+¶:^²ó]ÂF æ;w.Þ<>R…±Ôñ@µAúO=2ÿ½=+¸÷%<C'ðHCÈ¢ÉsŽÛ© 
]óŒìR®þ,Îá Ž<ª¨ãÐN³¥ù4n1<EFBFBD>² UjïˆÕÖÛÜÞB/¾.ôLtKM{îFqƒV2ù(Aˆµh+}<EFBFBD>»>vÄA¤Y¾d<EFBFBD>=V¦hÇÒwï¬Ï<EFBFBD>uÚâånÑÓ§^È­«=-­ˆ;¾h &#C²í óšáXx˜3Í-[z.œ2$kV©­\Þ^(~Ž<EFBFBD>ÇX¸¡vÑ Mb|ƒ'ÆŽ׫õÚŠŒ×³)¢™ ¤¥þP´5IÎf¸Õ Ë8IŽÎ#+34lý-ÊÐíhB4 <>µdÑ:> ó†Ô”uÏIFølDe<44>|ˆÁ<CB86>Tq©gÔå*j¿ÇÏ7‡F†ïÆ~<7E>6vÕ§_òö,¶P ´DáÛ ×b0Íçü_¨íçké·bÏñ™¦³€•JU4Ô¤´:Žà7|Ã3rN`â¾áÍHŒpŸrR\Ó+¹ìoqò4+ÁÒ·D°õÐĺÊ[Ã&*¯/Æ2Ñκ“ÅèIô2Z·# ã¾>÷Vv„FéœÑ<C593>•Y†ïÞ?JƒhDgû¢ÒÙõèÉãpR<70>„îkжŠ´¡3 \©»èXžš§>'æëä«´>×dFž<EFBFBD>ÐíÙ8O7æò~)%zݵî(y²ÓöU_˜MXiaªÇ]øcPeųÞÒq¹ËÞï<EFBFBD>ëAPØ*M´ǺØѤ fÞüFw-4ăñìÕ:×ùÚýº0^ñóy" “†Êm>±ná+¡z1¢Ü]Pn¸˜²„+syu×<75>Ÿ<­eq±Ï¢PÑç©ËÀĨ̡¬M²µy“ËÓëàÔŸû±f.‡'lÚözéI0 ~4é(%;);ÒICCJa„J|pŽŸiee¸<E280BA>¢•Å·¢”Ôp ‘ô–¬#£nÇ
ì­¹ç>G2
/ñs´]Pâú#4®?ßýŽbX5Œº©º¯ëQþÈóOKF"oæc÷ð<C3B7>ÓŠuéO«º»; H®[%PõÐ̯°ð4q'ÇÁ¸ôâИ)˜â˜¬×"ê¤ç7­¾ËZœÀ`q9i?ûS¢nÂkÁQhÏhË1Æs3|œ}S¦üØ<EFBFBD>èWÓ±PðäîaÑWÉJ<EFBFBD>àÔYaæ¦áÃŽÅ ³}霮»òÌÐ[ïYÂGpÑ*ñ<ŸªM[ÿSMDÓÃvÊHˆX­òŸ<EFBFBD> ë²Õô±¥\Þ#  xòyB!ÈÜÊè¹Û1»æþÐ+Ü<EFBFBD>n$ýçÌ Ñ5[Q69^ëÄ 6y16PIÍm]<EFBFBD>g_Ü ÓN׬qŒ®œÂ[ <EFBFBD>¤ª7ç2 ¯åÒĽU Ýé"Îðúéíåké·b(FzÿŒ„g ?¬‰¬¦éÍÀ†fB>…‰MXqLs5¸ßMaŠ×ë¿<C3AB>l¼¥ÞÃÏbúê° bYëñ‰5!Â×Ǧ‰DâÜt0ÞöEî”JOyÿâO-„Í‹ž`Éö3²@"ïh´ÙX.OÉ-o<EFBFBD>q^Ù¦é?¨Íu®úœh]è/¡¿åÂ}A´/Ø~ôÉ:åuó+5ÙrQS2Ò¢¹|-qná3PÈ lHLLBa2½_fÛw¬s,õ%44Ä ©9ôŸoÇp)/¬¸C bôGæÝoÕ¼ØÒç}þ
cÒ3ç¥ðPoù¥ŸK<iß7díîD\¹ÙzáàŠ»hj¢yö<EFBFBD>Ÿ¤Š 먧šÍÀå<¼Ò²@üb¾ÐÎ.×ÛÉb&§«%. d\æšrõUgy8¥`ìlõÎKuPÌëíf×ÙÌv[$ZÂL²Œ<EFBFBD>ÖÁÛÏ<EFBFBD>17t¤¢«ö¹¬ºB­æäî~!ì9·âs&é¨é"IqîÑQÏ?M26`H¢¾<>g!Ô&N-BôÚ{¦\›å'Oè(f×Ẫ¹ýÖ<C3BD>—D]{"Gš9Ã$Á++bB.Î8ß8E«D¸Ô«;U¹cYC%`ð®´Ü|Çe<EFBFBD>
ï:&ùf£Êá/#Ì>±H´®˜~^t&c%fßCé<EFBFBD>ù¡Þ×Î6ï©*ª˜^*§Š
ˆøÔF¾5¯ÓTN;ôYqªí7ò{7[&-Óyiœ0É)¬úª5ÕŒ<Û]ˆËT)¿œ^$(ØþvŸ½ ŒœBlv[Ì:ðÐ
Ê]*Vk'˜$0ÓI5è(Ø¢Òƒñ|y#I¹Ð©i ÛŒî(j¸‡*ö7_35´| é`E<>zÒ­[ãýdx5v1nöúõ<ã4Bø:ú4£¾28f£X ×`ò87AÇ5Øq<C398><71>àó,Y<XÜ{Æ×ÐJÉ“P™<50>%/ü°”VêÛõcÜMž0HÎÐüDô&éÜÞ‡Bÿ^Åî{_-žCDŒêÁPòš(hþXiü¼ìö0„™QQùêHæå®RÒ¨&>£#<•WQ2Ë<}ÿrU¶
/Š-Ž­¥{¢<EFBFBD>~)<EFBFBD>~ØŽ îçyÀŸ¥lI¿ÖY|pÌÙK´Ó±éSyC%.åÿìoO9X!-®¤LSý'æw1·:E6ñ¼œ†@©{Ð[ñ3<14>2“²kÊ*üþyTW*°?|.¦›ÂŸ&Iú¥þº?88E=õc£ §«!ìœn<Æ®h]_d´V±nßqƒ».ŸBÇŸåìvjï8#3¹»§¸¿\V®Ÿsþ¥“º<E2809C>Š}/Y¤· rõs™àEä3­7vûº. +%£Nm4lz"w<1A> úœ6ÔýÞ£¦«)¬>>Í)=`à‡YdßcßJé¡„wrqrô¶™Œ‡†‡ Ë®¡œ›œàö±°±3õØËÆÅÆH
íàÛÚÛ]õðïðr4
I,œ^A4/./±sVIDCDƈk^YXYÛ<EFBFBD>snmn𲈃ƒǪ<EFBFBD>˜˜Ü¿²­¬­/ñÔÇÂÁÂDéÜ×Ö×Yþñìëìn0ƒE(˜Z=0+*+­oRE@?@ÂgZUTU×|ojijì®~æØ»®©¨©+íÐþ½¾@åØÓÒÓUúíèçèj,ýüýA$V9,'&'©kNA<;<¾cVQPQÓxkfefèª<EFBFBD>{z{ý¿¢<EFBFBD><EFBFBD><EFBFBD>Ô·ª¥¤¥'éÌ¿º¹º<þáÔÏÎÏQöéäãäf( þùøù{=  <0E>R5(#"#¥gJ=878º|_RMLMÏtgbab䦉|wvwù»žŒŒг¦¡ ¡#åÈ»¶µ¶8úÝÐËÊËMòåàßàb$úõôõw9
ŒN1$¡cF9434x[NIHIË<EFBFBD>pc^]^à¢xsrsõ·š<EFBFBD>ˆˆ
̯¢<EFBFBD>œ<EFBFBD>áÄ·²±²4öÙÌÇÆÇI îáÜÛÜ^ öñðñs5 ˆJ- <EFBFBD>_B50/0²tWJEDEÇl_ZYZÜž<EFBFBD>tonoñ³ƒÈ«ž˜=£
--]]

BIN
preSH.tar.gz Normal file

Binary file not shown.

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-allmem.lua : Memory usage tester
-- Authors: 20kdc

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-eeprog.lua : Tiny EEPROM flasher
-- Authors: 20kdc

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-ghostcall.lua : Who are you gonna call?
-- Authors: 20kdc

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-metamachine.lua : Virtual Machine
-- Authors: 20kdc
@ -69,7 +65,6 @@ local screensAll = {
local tmpAddress = "k-tmpfs"
local passthroughs = {}
local fakeArch = _VERSION
local components = {
["k-computer"] = {
type = "computer",
@ -88,55 +83,6 @@ local components = {
getProgramLocations = function ()
-- Entries of {"file", "lootdisk"}
return {}
end,
getDeviceInfo = function ()
return {
["k-computer"] = {
["class"] = "system",
["description"] = "Computer",
["product"] = "Freeziflow Liquid-Cooling Unit",
["vendor"] = "KDC Subsystems",
["capacity"] = "10",
["width"] = "",
["clock"] = ""
},
["k-processor"] = {
["class"] = "processor",
["description"] = "CPU",
["product"] = "Celesti4 Quantum Computing System",
["vendor"] = "KDC Subsystems",
["capacity"] = "",
["width"] = "",
["clock"] = "9000"
},
["k-memory"] = {
["class"] = "memory",
["description"] = "Memory bank",
["product"] = "Lun4 Paging Subsystem",
["vendor"] = "KDC Subsystems",
["capacity"] = "",
["width"] = "",
["clock"] = "9000"
},
["k-gpu"] = {
["class"] = "display",
["description"] = "Graphics controller",
["product"] = "Tw1-l GPU Multiplexer",
["vendor"] = "KDC Subsystems",
["capacity"] = "8000",
["width"] = "8",
["clock"] = "9000"
}
}
end,
getArchitectures = function ()
return {fakeArch}
end,
setArchitecture = function (a)
fakeArch = a
end,
getArchitecture = function ()
return fakeArch
end
},
["k-gpu"] = libVGPU.newGPU(screensAll),
@ -178,8 +124,8 @@ vmComponent = {
return tk
end,
invoke = function (com, me, ...)
if not components[com] then error("no such component " .. com) end
if not components[com][me] then error("no such method " .. com .. "." .. me) end
if not components[com] then error("no such component") end
if not components[com][me] then error("no such method") end
return components[com][me](...)
end,
proxy = function (com)
@ -587,9 +533,6 @@ while ((not vmBaseCoroutine) or (coroutine.status(vmBaseCoroutine) ~= "dead")) a
break
elseif ev[3] == "line" then
screensInt[id][2].line(ev[4])
elseif ev[3] == "clipboard" then
table.insert(signalStack, {ev[3], screensInt[id][1] .. "-kb", ev[4], "neo"})
break
elseif ev[3] == "touch" or ev[3] == "drag" or ev[3] == "drop" or ev[3] == "scroll" then
local x = ev[4]
local y = ev[5]

View File

@ -1,353 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- app-nbcompose.lua : Music!
-- Authors: 20kdc
local nb = neo.requireAccess("c.iron_noteblock", "noteblocks").list()()
local ic = neo.requireAccess("x.neo.pub.base", "fs")
local event = require("event")(neo)
local neoux = require("neoux")(event, neo)
local iTranslation = {
[0] = 0, -- piano / air (def)
4, -- double bass / wood (def)
1, -- bass drum / stone (def)
2, -- snare drum / sand (def)
3, -- click / glass (def)
-- JUST GIVE UP
4, -- guitar / wool
5, -- flute / clay
6, -- bell / gold
6, -- chime / pice
6, -- xylo / bone
}
local instKey = {
[2] = 0,
[3] = 1,
[4] = 2,
[5] = 3,
[6] = 4,
[144] = 5,
[7] = 5,
[8] = 6,
[9] = 7,
[10] = 8,
[11] = 9
}
local noteKey = "1q2w3er5t6yu8i9o0pzsxdcvg"
-- Application State
local fileData
local uptime = os.uptime()
local songPosition = 0
local selectionL, selectionR = -8, -9
local running = true
local playing = false
local timerExistsFlag = false
local window
local defInst = 0
--
local tick -- Tick function for timer making
local file = require("knbs").new()
-- Window width is always 50. Height is layers + 3, for the top bar.
local theStatusBar, theNotePane, genMain
local function updateStatusAndPane()
if theStatusBar.update then theStatusBar.update(window) end
if theNotePane then
for _, v in ipairs(theNotePane) do
v.update(window)
end
end
end
local function commonKey(a, c, f)
if a == 32 then
playing = not playing
theStatusBar.update(window)
if playing then
if not timerExistsFlag then
uptime = os.uptime()
event.runAt(uptime, tick)
timerExistsFlag = true
end
end
elseif a == 91 then
selectionL = songPosition
updateStatusAndPane()
elseif a == 93 then
selectionR = songPosition
updateStatusAndPane()
elseif c == 203 and (f.shift or f.rshift) then
songPosition = 0
updateStatusAndPane()
elseif c == 205 and (f.shift or f.rshift) then
songPosition = file.length
updateStatusAndPane()
elseif c == 203 then
songPosition = math.max(0, songPosition - 1)
updateStatusAndPane()
elseif c == 205 then
songPosition = songPosition + 1
updateStatusAndPane()
end
end
theStatusBar = {
x = 1,
y = 3,
w = 50,
h = 1,
selectable = true,
line = function (window, x, y, lined, bg, fg, selected)
if selected then
bg, fg = fg, bg
end
window.span(x, y, ((playing and "Playing") or "Paused") .. " (SPACE) ; " .. (songPosition + 1) .. "/" .. file.length .. " ([Shift-]←/→)", bg, fg)
end,
key = function (window, update, a, c, d, f)
if not d then return end
commonKey(a, c, f)
end
}
local function genLayers()
theStatusBar.update = nil
theNotePane = nil
local layers = {}
for i = 1, file.height do
local layer = i - 1
table.insert(layers, neoux.tcfield(1, i + 1, 40, function (tx)
file.layers[layer][1] = tx or file.layers[layer][1]
return file.layers[layer][1]
end))
table.insert(layers, neoux.tcrawview(42, i + 1, {"Vol."}))
table.insert(layers, neoux.tcfield(46, i + 1, 5, function (tx)
if tx then
file.layers[layer][2] = math.max(0, math.min(255, math.floor(tonumber(tx) or 0)))
end
return tostring(file.layers[layer][2])
end))
end
return 50, file.height + 1, nil, neoux.tcwindow(50, file.height + 1, {
neoux.tcbutton(1, 1, "Purge Extra Layers", function (w)
local knbs = require("knbs")
local layerCount = knbs.correctSongLH(file)
knbs.resizeLayers(file, layerCount)
w.reset(genLayers())
end),
neoux.tcbutton(21, 1, "Del.Last", function (w)
require("knbs").resizeLayers(file, file.height - 1)
w.reset(genLayers())
end),
neoux.tcbutton(31, 1, "Append", function (w)
require("knbs").resizeLayers(file, file.height + 1)
w.reset(genLayers())
end),
table.unpack(layers)
}, function (w)
w.reset(genMain())
end, 0xFFFFFF, 0)
end
function genMain()
theNotePane = {}
for l = 1, file.height do
local layer = l - 1
theNotePane[l] = {
x = 1,
y = 3 + l,
w = 50,
h = 1,
selectable = true,
line = function (window, x, y, lined, bg, fg, selected)
if selected then
bg, fg = fg, bg
end
local text = ""
for i = 1, 5 do
local noteL, noteR = " ", " "
local tick = songPosition + i - 3
if songPosition == tick then
noteL = "["
noteR = "]"
end
if selectionR >= selectionL then
if selectionL == tick then
noteL = "{"
end
if selectionR == tick then
noteR = "}"
end
end
text = text .. noteL
local fd = file.ticks[tick]
fd = fd and fd[layer]
if fd then
text = text .. string.format(" %02i/%02i", fd[1], fd[2])
else
text = text .. " "
end
text = text .. noteR
end
window.span(x, y, text, bg, fg)
end,
key = function (window, update, a, c, d, f)
if not d then return end
commonKey(a, c, f)
if a == 8 then
if file.ticks[songPosition] then
file.ticks[songPosition][layer] = nil
require("knbs").correctSongLH(file)
update()
theStatusBar.update(window)
end
elseif instKey[c] and (f.shift or f.rshift) then
file.ticks[songPosition] = file.ticks[songPosition] or {}
defInst = instKey[c]
local nt = 45
if file.ticks[songPosition][layer] then
file.ticks[songPosition][layer][1] = defInst
nt = file.ticks[songPosition][layer][2]
end
if nb then
nb.playNote(iTranslation[defInst] or 0, nt - 33, file.layers[layer][2] / 100)
end
require("knbs").correctSongLH(file)
update()
theStatusBar.update(window)
elseif a >= 0 and a < 256 and noteKey:find(string.char(a), 1, true) then
file.ticks[songPosition] = file.ticks[songPosition] or {}
local note = noteKey:find(string.char(a), 1, true) - 1
file.ticks[songPosition][layer] = {defInst, note + 33}
if nb then
nb.playNote(iTranslation[defInst] or 0, note, file.layers[layer][2] / 100)
end
require("knbs").correctSongLH(file)
update()
theStatusBar.update(window)
elseif a == 123 then
if selectionR >= selectionL then
local storage = {}
for i = selectionL, selectionR do
storage[i] = file.ticks[i] and file.ticks[i][layer] and {table.unpack(file.ticks[i][layer])}
end
for i = selectionL, selectionR do
local p = songPosition + (i - selectionL)
file.ticks[p] = file.ticks[p] or {}
file.ticks[p][layer] = storage[i]
end
require("knbs").correctSongLH(file)
update()
theStatusBar.update(window)
end
end
end
}
end
-- We totally lie about the height here to tcwindow. "Bit of a cheat, but who's counting", anyone?
-- It is explicitly documented that the width and height are for background drawing, BTW.
return 50, file.height + 3, nil, neoux.tcwindow(50, 3, {
neoux.tcfield(1, 1, 20, function (tx)
file.name = tx or file.name
return file.name
end),
neoux.tcfield(21, 1, 15, function (tx)
file.transcriptor = tx or file.transcriptor
return file.transcriptor
end),
neoux.tcfield(36, 1, 15, function (tx)
file.songwriter = tx or file.songwriter
return file.songwriter
end),
neoux.tcbutton(1, 2, "New", function (w)
file = require("knbs").new()
songPosition = 0
playing = false
window.reset(genMain())
end),
neoux.tcbutton(6, 2, "Load", function (w)
neoux.fileDialog(false, function (f)
if not f then return end
file = nil
file = require("knbs").deserialize(f.read("*a"))
f.close()
songPosition = 0
playing = false
window.reset(genMain())
end)
end),
neoux.tcbutton(12, 2, "Save", function (w)
neoux.fileDialog(true, function (f)
if not f then return end
require("knbs").serialize(file, f.write)
f.close()
end)
end),
neoux.tcbutton(18, 2, "Ds.L", function (w)
neoux.fileDialog(false, function (f)
if not f then return end
file.description = f.read("*a")
f.close()
end)
end),
neoux.tcbutton(24, 2, "Ds.S", function (w)
neoux.fileDialog(true, function (f)
if not f then return end
f.write(file.description)
f.close()
end)
end),
neoux.tcbutton(30, 2, "Layers", function (w)
window.reset(genLayers())
end),
neoux.tcrawview(39, 2, {"cT/S"}),
neoux.tcfield(43, 2, 8, function (tx)
if tx then
local txn = tonumber(tx) or 0
file.tempo = math.min(math.max(0, math.floor(txn)), 65535)
end
return tostring(file.tempo)
end),
theStatusBar,
table.unpack(theNotePane)
}, function (w)
w.close()
running = false
end, 0xFFFFFF, 0)
end
function tick()
if playing then
-- Stop the user from entering such a low tempo that stuff freezes by:
-- 1. Stopping tempo from going too low to cause /0
-- 2. Ensuring timer is at most 1 second away
local temp = 1 / math.max(file.tempo / 100, 0.01)
if os.uptime() >= uptime + temp then
-- execute at this song position
if file.ticks[songPosition] and nb then
for i = 0, file.height - 1 do
local tck = file.ticks[songPosition][i]
if tck then
nb.playNote(iTranslation[tck[1]] or 0, tck[2] - 33, file.layers[i][2] / 100)
end
end
end
songPosition = songPosition + 1
if songPosition >= file.length then songPosition = 0 end
updateStatusAndPane()
uptime = uptime + temp
end
event.runAt(math.min(os.uptime() + 1, uptime + temp), tick)
else
timerExistsFlag = false
end
end
window = neoux.create(genMain())
while running do event.pull() end

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-nbox2018.lua : NODEBOX 2018
-- Authors: 20kdc

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-nprt2018.lua : 3D printing application
-- Authors: 20kdc

View File

@ -1,41 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- app-pclogix-upload: Upload to PCLogix Hastebin (paste.pc-logix.com)
local inet = neo.requireAccess("c.internet", "to upload").list()()
assert(inet, "no internet card")
local event = require("event")(neo)
local neoux = require("neoux")(event, neo)
local f = neoux.fileDialog(false)
if not f then return end
local data = f.read("*a")
f.close()
local s = inet.request("http://paste.pc-logix.com/documents", data)
assert(s, "no socket")
s.finishConnect()
local code, msg = s.response()
local res = tostring(code) .. " " .. tostring(msg) .. "\n"
while true do
local chk, err = s.read(neo.readBufSize)
if not chk then
res = res .. "\n" .. tostring(err)
break
end
if chk == "" then
event.sleepTo(os.uptime() + 0.05)
else
res = res .. chk
end
end
res = res .. "\nPrefix ID with http://paste.pc-logix.com/raw/"
neoux.startDialog(res, "result", true)
s.close()
-- that's a hastebin client for OC!
-- done! now I'll upload this with itself.
-- - 20kdc

View File

@ -1,66 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- app-rsctrl: redstone control
local event = require("event")(neo)
local neoux = require("neoux")(event, neo)
local rs = neo.requireAccess("c.redstone", "redstone control")
neo.requireAccess("s.h.redstone_changed", "updating to new input")
local running = true
local window
--123456789012345678901234567890123456789012345
--PPPPPPPP-PPPP-PPPP-PPPPPPPPPPPP Wake=[x]
-- D=y[x] U=y[x] N=y[x] S=y[x] W=y[x] E=y[x]
local function mainGen()
local ctrls = {}
local prs = 0
for pri in rs.list() do
prs = prs + 1
local ins = {}
local outs = {}
local wt = pri.getWakeThreshold()
for i = 0, 5 do
ins[i + 1] = pri.getInput(i)
outs[i + 1] = pri.getOutput(i)
end
table.insert(ctrls, neoux.tcrawview(1, (prs * 2) - 1, {
unicode.safeTextFormat(pri.address),
string.format(" D=%01x U=%01x N=%01x S=%01x W=%01x E=%01x", table.unpack(ins))
}))
table.insert(ctrls, neoux.tcrawview(38, (prs * 2) - 1, {
"Wake="
}))
table.insert(ctrls, neoux.tcfield(43, (prs * 2) - 1, 3, function (tx)
if tx then
wt = math.floor(tonumber("0x" .. tx:sub(-1)) or 0)
pri.setWakeThreshold(wt)
end
return string.format("%01x", wt)
end))
for i = 0, 5 do
table.insert(ctrls, neoux.tcfield(6 + (i * 7), prs * 2, 3, function (tx)
if tx then
outs[i + 1] = tonumber("0x" .. tx:sub(-1)) or 0
pri.setOutput(i, outs[i + 1])
end
return string.format("%01x", outs[i + 1])
end))
end
end
return 45, prs * 2, nil, neoux.tcwindow(45, prs * 2, ctrls, function ()
window.close()
running = false
end, 0xFFFFFF, 0)
end
window = neoux.create(mainGen())
while running do
local hv = event.pull()
if hv == "redstone_changed" then
window.reset(mainGen())
end
end

View File

@ -1,367 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- app-tapedeck.lua : Computronics Tape interface.
-- Added note: Computerized record discs aren't available, so it can't be called vinylscratch.
-- Authors: 20kdc
local tapeRate = 4096
local tapeAccess = neo.requireAccess("c.tape_drive", "tapedrives")
local event = require("event")(neo)
local neoux = require("neoux")(event, neo)
-- There's no way to get these, so they get reset
local pcvals = {vol = 100, spd = 100}
local function pcbox(x, y, low, high, id, fun)
return neoux.tcfield(x, y, 5, function (tx)
if tx then
pcvals[id] = math.min(math.max(0, math.floor(tonumber(tx) or 0)), high)
fun(math.max(pcvals[id], low) / 100)
end
return tostring(pcvals[id])
end)
end
local window
local running = true
local focused = true
local updateTick
local downloadCancelled = false
local genPlayer, genList -- used to return to player
local function genDownloading(downloadText, inst)
downloadCancelled = false
local lclLabelText = {downloadText}
local lclLabel = neoux.tcrawview(1, 1, lclLabelText)
local thr = {
"/",
"-",
"\\",
"|"
}
local thri = 0
updateTick = function ()
lclLabelText[1] = downloadText .. " " .. (inst.getPosition() / (1024 * 1024)) .. "MB " .. thr[(thri % #thr) + 1]
thri = thri + 1
lclLabel.update(window)
end
return 40, 1, nil, neoux.tcwindow(40, 1, {
lclLabel
}, function (w)
downloadCancelled = true
end, 0xFFFFFF, 0)
end
local function maybeSleep()
if math.random() > 0.98 then
event.sleepTo(os.uptime() + 0.05)
end
end
local function doINetThing(inet, url, inst)
inet = inet.list()()
assert(inet, "No available card")
inst.stop()
inst.seek(-inst.getSize())
window.reset(genDownloading("downloading...", inst))
local req = assert(inet.request(url))
req.finishConnect()
local tapePos = 0
local tapeSize = inst.getSize()
while (not downloadCancelled) and tapePos < tapeSize do
local n, n2 = req.read(neo.readBufSize)
if not n then
if n2 then
req.close()
error(n2)
end
break
elseif n == "" then
event.sleepTo(os.uptime() + 0.05)
else
inst.write(n)
tapePos = tapePos + #n
end
end
req.close()
inst.seek(-inst.getSize())
end
local function genWeb(inst)
updateTick = nil
local url = ""
local lockout = false
return 40, 3, nil, neoux.tcwindow(40, 3, {
neoux.tcrawview(1, 1, {"URL to write to tape?"}),
neoux.tcfield(1, 2, 40, function (t)
url = t or url
return url
end),
neoux.tcbutton(1, 3, "Download & Write", function (w)
lockout = true
local inet = neo.requestAccess("c.internet")
lockout = false
if inet then
local ok, err = pcall(doINetThing, inet, url, inst)
if not ok then
neoux.startDialog("Couldn't download: " .. tostring(err), "error")
end
end
w.reset(genPlayer(inst))
end)
}, function (w)
w.reset(genPlayer(inst))
end, 0xFFFFFF, 0)
end
-- The actual main UI --
genPlayer = function (inst)
local cachedLabel = inst.getLabel() or ""
local cachedState = inst.getState()
local function pausePlay()
if inst.getState() == "PLAYING" then
inst.stop()
else
inst.play()
end
window.reset(genPlayer(inst))
end
-- Common code for reading/writing tapes.
local function rwButton(mode)
local fh = neoux.fileDialog(mode)
if not fh then return end
inst.stop()
local tapeSize = inst.getSize()
inst.seek(-tapeSize)
local tapePos = 0
window.reset(genDownloading("working...", inst))
while tapePos < tapeSize and not downloadCancelled do
if mode then
local data = inst.read(neo.readBufSize)
if not data then break end
tapePos = tapePos + #data
local res, ifo = fh.write(data)
if not res then
neoux.startDialog(tostring(ifo), "issue")
break
end
else
local data = fh.read(neo.readBufSize)
if not data then break end
tapePos = tapePos + #data
inst.write(data)
end
maybeSleep()
end
inst.seek(-tapeSize)
fh.close()
window.reset(genPlayer(inst))
end
local elems = {
neoux.tcrawview(1, 1, {
"Label:"
}),
neoux.tcfield(7, 1, 34, function (tx)
if tx then
inst.setLabel(tx)
cachedLabel = tx
end
return cachedLabel
end),
{
x = 1,
y = 5,
w = 40,
h = 1,
selectable = true,
line = function (w, x, y, lined, bg, fg, selected)
local lx = ""
local pos = inst.getPosition()
local sz = inst.getSize()
if inst.isReady() then
-- Show a bar
local tick = sz / 23
for i = 1, 23 do
local alpos = (tick * i) - (tick / 2)
if pos > alpos then
lx = lx .. "="
else
lx = lx .. "-"
end
end
else
lx = "NO TAPE HERE."
end
local sec = pos / tapeRate
local secz = sz / tapeRate
lx = lx .. string.format(" %03i:%02i / %03i:%02i ",
math.floor(sec / 60), math.floor(sec) % 60,
math.floor(secz / 60), math.floor(secz) % 60)
if selected then bg, fg = fg, bg end
window.span(x, y, lx, bg, fg)
end,
key = function (w, update, a, b, c, kf)
local amount = tapeRate * 10
if kf.shift or kf.rshift then
amount = amount * 24
end
if c then
if a == 32 then
pausePlay()
elseif b == 203 then
inst.seek(-amount)
update()
return true
elseif b == 205 then
inst.seek(amount)
update()
return true
end
end
end
},
neoux.tcrawview(33, 3, {
"% Volume"
}),
neoux.tcrawview(20, 3, {
"% Speed"
}),
pcbox(15, 3, 25, 200, "spd", inst.setSpeed),
pcbox(28, 3, 0, 100, "vol", inst.setVolume),
neoux.tcrawview(1, 4, {
"Seeker: use ◃/▹ (shift goes faster)"
}),
neoux.tcbutton(1, 3, "«", function (w)
inst.seek(-inst.getSize())
end),
neoux.tcbutton(11, 3, "»", function (w)
inst.seek(inst.getSize())
end),
neoux.tcbutton(4, 3, ((inst.getState() == "PLAYING") and "Pause") or "Play", function (w)
pausePlay()
end),
-- R/W buttons
neoux.tcbutton(1, 2, "Read", function (w)
rwButton(true)
end),
neoux.tcbutton(7, 2, "Write", function (w)
rwButton(false)
end),
neoux.tcbutton(14, 2, "Write Web", function (w)
w.reset(genWeb(inst))
end),
neoux.tcbutton(25, 2, "Copy", function (w)
w.reset(genList(function (inst2)
local ts1 = inst.getSize()
inst.stop()
inst.seek(-ts1)
local ts2 = inst2.getSize()
inst2.stop()
inst2.seek(-ts2)
if ts1 < ts2 then
w.reset(genDownloading("copying...", inst))
else
w.reset(genDownloading("copying...", inst2))
end
local pos = 0
while pos < ts1 and pos < ts2 and not downloadCancelled do
local dat = inst.read(neo.readBufSize)
inst2.write(dat)
pos = pos + #dat
maybeSleep()
end
inst.seek(-ts1)
inst2.seek(-ts2)
inst2.setLabel((inst.getLabel() or "") .. " Copy")
w.reset(genPlayer(inst))
end))
end),
neoux.tcbutton(31, 2, "Erase", function (w)
local ts1 = inst.getSize()
inst.stop()
inst.seek(-ts1)
w.reset(genDownloading("erasing...", inst))
local blank = ("\x00"):rep(neo.readBufSize)
local pos = 0
while pos < ts1 and not downloadCancelled do
inst.write(blank)
pos = pos + #blank
maybeSleep()
end
inst.seek(-ts1)
w.reset(genPlayer(inst))
end)
}
updateTick = function ()
local lcl = cachedLabel
cachedLabel = inst.getLabel() or ""
elems[3].update(window)
if inst.getState() ~= cachedState then
window.reset(genPlayer(inst))
elseif lcl ~= cachedLabel then
elems[2].update(window)
end
end
local n = neoux.tcwindow(40, 5, elems, function (w)
updateTick = nil
running = false
w.close()
end, 0xFFFFFF, 0)
return 40, 5, inst.address, function (a, ...)
if a == "focus" then
focused = (...) or true
end
return n(a, ...)
end
end
genList = function(callback)
updateTick = nil
local elems = {}
local tapes = {}
for v in tapeAccess.list() do
table.insert(tapes, v)
end
for k, v in ipairs(tapes) do
-- There's 38 chars available...
local desc1 = neoux.pad(v.address, 13, false, true)
if v.isReady() then
desc1 = desc1 .. ": " .. neoux.pad(v.getLabel() or "", 23, false, true)
else
desc1 = desc1 .. " (no tape)"
end
elems[k] = neoux.tcbutton(1, k, desc1, function (w)
callback(v)
end)
end
return 40, #elems, nil, neoux.tcwindow(40, #elems, elems, function (w)
running = false
w.close()
end, 0xFFFFFF, 0)
end
window = neoux.create(genList(function (v)
window.reset(genPlayer(v))
end))
-- Timer for time update
local function tick()
if updateTick then
updateTick()
end
event.runAt(os.uptime() + ((focused and 1) or 10), tick)
end
event.runAt(0, tick)
while running do
event.pull()
end

View File

@ -1,100 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- app-telnet.lua : just a utility now
-- Authors: 20kdc
local inet = neo.requireAccess("c.internet", "internet").list()()
local _, _, termId = ...
local ok = pcall(function ()
assert(string.sub(termId, 1, 12) == "x.neo.pub.t/")
end)
local termClose
if not ok then
termId = nil
assert(neo.executeAsync("svc-t", function (res)
termId = res.access
termClose = res.close
neo.scheduleTimer(0)
end, "kmt"))
while true do
if coroutine.yield() == "k.timer" then break end
end
end
local term = neo.requireAccess(termId, "terminal")
term.write([[
KittenOS NEO MUD Terminal
export TERM=ansi.sys <- IMPORTANT!!!
Enter target server:port...
]])
local targetBuffer = ""
neo.scheduleTimer(0)
while true do
local e = {coroutine.yield()}
if e[1] == "k.timer" then
while tcp do
local b, e = tcp.read(neo.readBufSize)
if not b then
if e then
term.write("\nkmt: " .. tostring(e) .. "\n")
tcp.close()
tcp = nil
end
elseif b == "" then
break
else
term.write(b)
end
end
neo.scheduleTimer(os.uptime() + 0.049)
elseif e[1] == "k.procdie" then
if e[3] == term.pid then
break
end
elseif e[1] == termId then
if targetBuffer and e[2] == "data" then
targetBuffer = targetBuffer .. e[3]:gsub("\r", "")
local p = targetBuffer:find("\n")
if p then
local ok, res, rer = pcall(inet.connect, targetBuffer:sub(1, p - 1))
targetBuffer = targetBuffer:sub(p + 1):gsub("\n", "\r\n")
if not ok then
-- Likes to return this kind
term.write("kmt: " .. tostring(res) .. "\n")
elseif not res then
-- Could theoretically return this kind
term.write("kmt: " .. tostring(rer) .. "\n")
else
-- Hopefully this kind
term.write("kmt: Connecting...\n")
tcp = res
tcp.write(targetBuffer)
targetBuffer = nil
end
end
elseif tcp and e[2] == "data" or e[2] == "telnet" then
tcp.write(e[3])
end
end
end
if tcp then
tcp.close()
end

View File

@ -1,53 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- svc-vdrslamp.lua : Virtual Redstone Lamp
-- Authors: 20kdc
local vdev = neo.requireAccess("x.svc.virtudev", "lamp dev")
local evr = neo.requireAccess("x.neo.pub.window", "the lamp")
local wnd = evr(12, 6)
local bLine = (" "):rep(12)
local function blank()
return 0
end
local total = 0
vdev.install({
type = "redstone",
address = "vdrslamp-" .. neo.pid,
slot = 0,
getWakeThreshold = blank,
setWakeThreshold = blank,
getInput = blank,
getOutput = function (i)
return total
end,
setOutput = function (i, v)
total = v
wnd.setSize(12, 6)
end
})
while true do
local e = {coroutine.yield()}
if e[1] == "x.neo.pub.window" then
if e[3] == "close" then
-- the ignorance of unregistration is deliberate
-- a working impl. will properly recover
return
elseif e[3] == "line" then
local bg = 0xFFFFFF
if total == 0 then bg = 0 end
wnd.span(1, e[4], bLine, bg, bg)
end
end
end

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- svc-ghostie.lua : Ghostie the test ghost!
-- Authors: 20kdc

View File

@ -1,160 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- svc-virtudev.lua : Virtual Device interface
-- Authors: 20kdc
local ic = neo.requireAccess("x.neo.pub.base", "to lock x.svc.virtudev")
-- this is a pretty powerful permission, and PROBABLY EXPLOITABLE
ic.lockPerm("x.svc.virtudev")
local r = neo.requireAccess("r.svc.virtudev", "api endpoint")
local root = neo.requireAccess("k.root", "the ability to modify the component API")
local proxies = {}
local types = {}
local function uninstall(k)
proxies[k] = nil
types[k] = nil
root.computer.pushSignal("component_removed", k, types[k])
end
local users = {}
local userCount = 0
r(function (pkg, pid, sendSig)
local userAddresses = {}
users[pid] = userAddresses
userCount = userCount + 1
return {
install = function (proxy)
local proxyAddress = proxy.address
local proxyType = proxy.type
assert(type(proxyAddress) == "string")
assert(type(proxyType) == "string")
assert(not proxies[proxyAddress], "proxy address in use: " .. proxyAddress)
proxies[proxyAddress] = proxy
types[proxyAddress] = proxyType
userAddresses[proxyAddress] = true
root.computer.pushSignal("component_added", proxyAddress, proxyType)
return function (a, ...)
root.computer.pushSignal(a, proxyAddress, ...)
end
end,
uninstall = function (k)
if userAddresses[k] then
uninstall(k)
userAddresses[k] = nil
end
end
}
end)
local componentProxyRaw = root.component.proxy
local componentTypeRaw = root.component.type
local componentMethodsRaw = root.component.methods
local componentFieldsRaw = root.component.fields
local componentDocRaw = root.component.doc
local componentInvokeRaw = root.component.invoke
local componentListRaw = root.component.list
root.component.proxy = function (address)
if proxies[address] then
return proxies[address]
end
return componentProxyRaw(address)
end
root.component.type = function (address)
if types[address] then
return types[address]
end
return componentTypeRaw(address)
end
local function methodsFieldsHandler(address, methods)
if proxies[address] then
local mt = {}
for k, v in pairs(proxies[address]) do
if (type(v) == "function") == methods then
mt[k] = true
end
end
return mt
end
if methods then
return componentMethodsRaw(address)
else
return componentFieldsRaw(address)
end
end
root.component.methods = function (address) return methodsFieldsHandler(address, true) end
root.component.fields = function (address) return methodsFieldsHandler(address, false) end
root.component.doc = function (address, method)
if proxies[address] then
return tostring(proxies[address][method])
end
return componentDocRaw(address, method)
end
root.component.invoke = function (address, method, ...)
if proxies[address] then
return proxies[address][method](...)
end
return componentInvokeRaw(address, method, ...)
end
root.component.list = function (f, e)
local iter = componentListRaw(f, e)
local ended = false
local others = {}
for k, v in pairs(types) do
if (f == v) or ((not e) and v:find(f, 1, true)) then
table.insert(others, {k, v})
end
end
return function ()
if not ended then
local a, t = iter()
if not a then
ended = true
else
return a, t
end
end
-- at end of that, so what about others
local ent = table.remove(others, 1)
if ent then
return table.unpack(ent)
end
end
end
while true do
local e1, e2, e3 = coroutine.yield()
if e1 == "k.procdie" then
if users[e3] then
for k, _ in pairs(users[e3]) do
uninstall(k)
end
users[e3] = nil
userCount = userCount - 1
if userCount == 0 then
break
end
end
end
end
root.component.proxy = componentProxyRaw
root.component.type = componentTypeRaw
root.component.methods = componentMethodsRaw
root.component.fields = componentFieldsRaw
root.component.doc = componentDocRaw
root.component.invoke = componentInvokeRaw
root.component.list = componentListRaw

View File

@ -0,0 +1,183 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- local.lua : CLAW Repository Metadata
-- Authors: 20kdc
return {
["app-eeprog"] = {
desc = "Example program: EEPROM programmer / copier",
v = 0,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-eeprog.lua",
"docs/repoauthors/app-eeprog"
},
},
["neo-docs"] = {
desc = "KittenOS NEO system documentation",
v = 4,
deps = {
"zzz-license-pd"
},
dirs = {
"docs",
"docs/repoauthors"
},
files = {
"docs/an-intro",
"docs/kn-intro",
"docs/kn-refer",
"docs/kn-sched",
"docs/kn-perms",
"docs/us-perms",
"docs/us-nxapp",
"docs/us-setti",
"docs/us-evrst",
"docs/ul-seria",
"docs/ul-fwrap",
"docs/ul-event",
"docs/ul-fmttx",
"docs/ul-neoux",
"docs/ul-brail",
"docs/ul-bmp__",
"docs/gp-pedan",
"docs/repoauthors/neo-docs"
},
},
["app-nbox2018"] = {
desc = "NBOX2018 and NPRT2018, a 3D-printing toolbox",
v = 0,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-nbox2018.lua",
"apps/app-nprt2018.lua",
"docs/repoauthors/app-nbox2018"
},
},
["app-allmem"] = {
desc = "Near-reproducible memory usage figures",
v = 0,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-allmem.lua",
"docs/repoauthors/app-allmem"
},
},
["svc-ghostie"] = {
desc = "Application that schedules a scare after a random time to test svc autostart",
v = 0,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/svc-ghostie.lua",
"apps/app-ghostcall.lua",
"docs/repoauthors/svc-ghostie"
},
},
["app-metamachine"] = {
desc = "Virtual machine",
v = 2,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"libs",
"docs",
"docs/repoauthors",
"data",
"data/app-metamachine"
},
files = {
"apps/app-metamachine.lua",
"libs/metamachine-vgpu.lua",
"libs/metamachine-vfs.lua",
"docs/repoauthors/app-metamachine",
"data/app-metamachine/confboot.lua",
"data/app-metamachine/lucaboot.lua"
},
},
["app-launchbar"] = {
desc = "Application launcher bar",
v = 0,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-launchbar.lua",
"docs/repoauthors/app-launchbar"
},
},
["app-slaunch"] = {
desc = "Searching launcher",
v = 0,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/app-slaunch.lua",
"docs/repoauthors/app-slaunch"
},
},
-- licenses (MUST BE IMMUTABLE)
["zzz-license-pd"] = {
desc = "license file 'Public Domain'",
v = 0,
deps = {
},
dirs = {
"docs",
"docs/licensing",
"docs/repoauthors"
},
files = {
"docs/licensing/Public Domain",
"docs/repoauthors/zzz-license-pd"
},
}
}

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- confboot.lua : VM configuration program
-- Authors: 20kdc

View File

@ -1,9 +1,5 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- lucaboot.lua : Fake EEPROM for VM.
-- Authors: 20kdc

View File

@ -53,9 +53,9 @@ Here, "*" means that everything after
program to start up.
"k.computer": The "computer" table,
with wrapMeta applied.
The security check may be aliased to
the "k.root" permission in future.
with wrapMeta applied,
pullSignal removed,
and also pushSignal.
"k.kill": function (pid) to kill any
process on the system.
@ -63,8 +63,7 @@ The security check may be aliased to
"r.*": Registers a service's API for
retrieval via the "x." mechanism.
Returns a:
function (function (pkg, pid, send),
secret)
function (function (pkg, pid, send))
While the registration is locked on
success, attempting to use it will
fail, as no handler has been given.
@ -72,11 +71,6 @@ The security check may be aliased to
registration with a callback used
for when a process tries to use the
registered API.
Unless 'secret' is truthy, a
k.registration event is sent to all
processes; using the secret flag is
useful for a more ad-hoc security
approach.
What that API returns goes to the
target process.
The given "sendSig" function can be

View File

@ -1,24 +0,0 @@
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
--
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-- THIS SOFTWARE.
These words in the given order with
the given full stops, quotes and
commas (newlines and code comment
tags such as "--" shall be ignored)
will be referred to as
"KittenOS NEO BSD0" in the project.
It is intended to be equivalent to
CC0, and whichever interpretation is
less restrictive should be favoured.
The reason it is used rather than CC0
is simply that it is smaller, which
is somewhat important in a memory-
limited system that KittenOS NEO is
designed to run within.

View File

@ -1,2 +1,2 @@
repository/apps/app-allmem.lua: 20kdc, KittenOS NEO BSD0
repository/apps/app-allmem.lua: 20kdc, Public Domain

Some files were not shown because too many files have changed in this diff Show More