Compare commits

...

27 Commits

Author SHA1 Message Date
20kdc 8bd316338b Merge branch 'master' into repository 2020-04-03 14:51:06 +01:00
20kdc 3dbea9d3f7 app-telnet: Add missing dependency on svc-t 2020-04-02 22:08:43 +01:00
20kdc 2ebc0840ac Rename app-kmt to app-telnet 2020-04-02 22:07:14 +01:00
20kdc da58c88d0d quick-fix DEFLATE decompressor so stuff runs on T1 again 2020-04-02 22:06:49 +01:00
20kdc fe17b0fb93 Finally settle on an exact level of support 2020-04-02 21:39:30 +01:00
20kdc c9157a1b7c More terminal improvements. Yes. Again. 2020-04-02 14:33:35 +01:00
20kdc ca4b6e5df2 Terminal polish: I might be going a tad too far with this 2020-04-02 12:15:09 +01:00
20kdc 7d1f6d2cae A bit of terminal polish 2020-04-02 01:52:25 +01:00
20kdc a585ce4a75 svc-t: Just realized will/won't need responses too 2020-04-02 00:15:36 +01:00
20kdc 3ed1cebe25 svc-t: Properly ignore TELNET subnegotiations 2020-04-02 00:08:28 +01:00
20kdc 7c70a1128c Bugfixes to Everest, finalize the terminal API 2020-04-01 23:24:20 +01:00
20kdc 5ac8f9ff11 Limited terminal line history 2020-03-31 18:28:59 +01:00
20kdc f761ad5824 Remove overwrite confirmations because life is short and UX nitpick bugs are long.
Have fun with this. If anyone complains I'm calling it the April 1st release.
2020-03-31 18:10:55 +01:00
20kdc aef0043d4a Finish up (maybe) the new compression scheme. 2020-03-31 17:52:24 +01:00
20kdc 151097cdce Finish purging deprecated lexcrunch stuff from the DEFLATE decompressor 2020-03-31 15:01:46 +01:00
20kdc bfcb4f9028 Smooth off the DEFLATE decompressor's sharp edges 2020-03-31 14:05:58 +01:00
20kdc cc55e8a66f Improve lexcrunch just a bit 2020-03-31 13:23:52 +01:00
20kdc 3c4a3147c4 The DEFLATE Decompressor (Of Pain And Suffering) 2020-03-31 12:00:06 +01:00
20kdc bc4d626b4e All the architectural changes pre-DEFLATE 2020-03-31 11:59:58 +01:00
20kdc dd21abe8fa More elseifs to feed DEFLATE, despite the negative effects on BDIVIDE 2020-03-30 20:21:41 +01:00
20kdc 22c1c211ef Bugfix: fix 'delete' key, fix Everest KB/M matching 2020-03-30 19:11:50 +01:00
20kdc 7840f0a231 Improve BDIVIDE (88 bytes free) 2020-03-30 17:55:20 +01:00
20kdc 339571ee9b Even more line editing improvements 2020-03-30 14:36:33 +01:00
20kdc 479412a5bb Better line editing 2020-03-30 12:08:12 +01:00
20kdc d2ee505316 Bigram compression for fun and size reduction 2020-03-29 22:43:38 +01:00
20kdc f5ba0489b2 Start work on r9 with the basic terminal system design 2020-03-28 19:13:03 +00:00
20kdc 20c016f068 Document svc-virtudev 2020-03-28 19:12:31 +00:00
54 changed files with 2473 additions and 1451 deletions

6
.gitignore vendored
View File

@ -1,8 +1,6 @@
# 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
@ -32,7 +30,9 @@ laboratory/*/*/*/*
inst.lua
# Available as the respective release
inst-gold.lua
com2/code.tar.bd
# Compression stuff
inst/iSymTab
# internal
upldr.sh
upldr-dev.sh
upldr-gold.sh

View File

@ -3,7 +3,7 @@
return {
["neo"] = {
desc = "KittenOS NEO Kernel & Base Libs",
v = 8,
v = 9,
deps = {
},
dirs = {
@ -18,6 +18,7 @@ return {
"libs/serial.lua",
"libs/fmttext.lua",
"libs/neoux.lua",
"libs/lineedit.lua",
"libs/braille.lua",
"libs/bmp.lua",
"libs/sys-filewrap.lua",
@ -54,7 +55,7 @@ return {
},
["neo-everest"] = {
desc = "KittenOS NEO / Everest (windowing)",
v = 5,
v = 9,
deps = {
"neo"
},
@ -67,7 +68,7 @@ return {
},
["neo-icecap"] = {
desc = "KittenOS NEO / Icecap",
v = 8,
v = 9,
deps = {
"neo"
},
@ -84,7 +85,7 @@ return {
},
["neo-secpolicy"] = {
desc = "KittenOS NEO / Secpolicy",
v = 8,
v = 9,
deps = {
},
dirs = {
@ -96,7 +97,7 @@ return {
},
["neo-coreapps"] = {
desc = "KittenOS NEO Core Apps",
v = 5,
v = 9,
deps = {
"neo"
},
@ -125,7 +126,7 @@ return {
},
["neo-logo"] = {
desc = "KittenOS NEO Logo (data)",
v = 8,
v = 9,
deps = {
},
dirs = {
@ -175,6 +176,20 @@ return {
"apps/svc-app-claw-worker.lua"
},
},
["svc-t"] = {
desc = "KittenOS NEO Terminal System",
v = 9,
deps = {
"neo"
},
dirs = {
"apps"
},
files = {
"apps/svc-t.lua",
"apps/app-luashell.lua"
},
},
["neo-meta"] = {
desc = "KittenOS NEO: Use 'All' to install to other disks",
v = 5,
@ -190,6 +205,7 @@ return {
"app-bmpview",
"app-flash",
"app-claw",
"svc-t",
"app-wget"
},
dirs = {

View File

@ -24,7 +24,7 @@ return {
},
["neo-docs"] = {
desc = "KittenOS NEO system documentation",
v = 8,
v = 9,
deps = {
"zzz-license-pd"
},
@ -43,6 +43,7 @@ return {
"docs/us-setti",
"docs/us-evrst",
"docs/us-clawf",
"docs/us-termi",
"docs/ul-seria",
"docs/ul-fwrap",
"docs/ul-event",
@ -50,6 +51,7 @@ return {
"docs/ul-neoux",
"docs/ul-brail",
"docs/ul-bmp__",
"docs/ul-linee",
"docs/gp-pedan",
"docs/repoauthors/neo-docs"
},
@ -89,11 +91,12 @@ return {
"docs/repoauthors/app-allmem"
},
},
["app-kmt"] = {
desc = "Line-terminal for MUDs & such",
v = 1,
["app-telnet"] = {
desc = "TELNET client",
v = 0,
deps = {
"neo",
"svc-t",
"zzz-license-pd"
},
dirs = {
@ -102,8 +105,8 @@ return {
"docs/repoauthors"
},
files = {
"apps/app-kmt.lua",
"docs/repoauthors/app-kmt"
"apps/app-telnet.lua",
"docs/repoauthors/app-telnet"
},
},
["svc-ghostie"] = {
@ -251,24 +254,6 @@ return {
"docs/repoauthors/app-slaunch"
},
},
["svc-virtudev"] = {
desc = "a clone of vcomponent",
v = 0,
deps = {
"neo",
"zzz-license-pd"
},
dirs = {
"apps",
"docs",
"docs/repoauthors"
},
files = {
"apps/svc-virtudev.lua",
"apps/app-vdrslamp.lua",
"docs/repoauthors/svc-virtudev"
},
},
-- libraries
["lib-knbs"] = {
desc = "NBS reader/writer library",
@ -286,6 +271,25 @@ return {
"docs/repoauthors/lib-knbs"
},
},
["svc-virtudev"] = {
desc = "a clone of vcomponent",
v = 1,
deps = {
"neo",
"zzz-license-pd"
},
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'",

View File

@ -226,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" then
if id == "set_setting" and currentGen ~= logGen then
window.reset(currentGen())
end
end

164
code/apps/app-luashell.lua Normal file
View File

@ -0,0 +1,164 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
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

@ -30,32 +30,17 @@ 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 cFlash = true
local ctrlFlag, focFlag, appendFlag
local ctrlFlag, appendFlag
local dialogLock = 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)
@ -139,31 +124,14 @@ local function getline(y)
-- rX is difficult!
local rX = 1
local Xthold = math.max(1, math.floor(sW / 2) - 1)
local _, cursorXP = unicode.safeTextFormat(lines[cursorY], cursorX)
local cLine, 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)
-- <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
return lineEdit.draw(sW, line, rY == cursorY and cursorXP, rX)
end
local function delLine()
local contents = lines[cursorY]
@ -179,22 +147,7 @@ local function delLine()
end
return contents
end
-- 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)
local function key(ks, kc, down)
if dialogLock then
return false
end
@ -212,18 +165,22 @@ local function key(ka, kc, down)
sH = 1
end
sW, sH = window.setSize(sW, sH)
return false
elseif kc == 208 then -- Down
sH = sH + 1
sW, sH = window.setSize(sW, sH)
return false
elseif 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
sW = sW + 1
sW, sH = window.setSize(sW, sH)
return false
elseif kc == 14 then -- ^Backspace
delLine()
return true
@ -240,7 +197,7 @@ local function key(ka, kc, down)
if cursorY < 1 then
cursorY = 1
end
clampCursorX()
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
return true
elseif kc == 208 or kc == 209 then -- Go down one - go down page
local moveAmount = 1
@ -251,36 +208,7 @@ local function key(ka, kc, down)
if cursorY > #lines then
cursorY = #lines
end
clampCursorX()
return true
elseif 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
elseif 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
elseif kc == 207 then
cursorX = unicode.len(lines[cursorY]) + 1
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
return true
end
-- Major Actions
@ -291,10 +219,13 @@ local function key(ka, kc, down)
return true
elseif kc == 61 then -- F3
startLoad()
return false
elseif kc == 62 then -- F4
startSave()
return false
elseif kc == 63 then -- F5
clipsrc.setSetting("clipboard", lines[cursorY])
return false
elseif kc == 64 then -- F6
local tx = clipsrc.getSetting("clipboard") or ""
local txi = tx:find("\n")
@ -311,6 +242,7 @@ local function key(ka, kc, down)
return true
elseif kc == 65 then -- F7
appendFlag = false
return false
elseif kc == 66 then -- F8
if appendFlag then
local base = clipsrc.getSetting("clipboard")
@ -322,29 +254,37 @@ local function key(ka, kc, down)
return true
end
end
-- 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
-- LEL Keys
local lT, lC, lX = lineEdit.key(ks, kc, lines[cursorY], cursorX)
if lT then
lines[cursorY] = lT
end
if ka ~= 0 then
putLetter(unicode.char(ka))
return true
if lC then
cursorX = lC
end
return false
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
end
flush = function ()
@ -353,16 +293,9 @@ flush = function ()
end
end
neo.scheduleTimer(os.uptime() + 0.5)
while true do
local e = {coroutine.yield()}
if e[1] == "k.timer" and e[2] == focFlag then
cFlash = not cFlash
local csY = math.ceil(sH / 2)
window.span(1, csY, getline(csY), 0xFFFFFF, 0)
focFlag = neo.scheduleTimer(os.uptime() + 0.5)
elseif e[1] == "x.neo.pub.window" then
if 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)
@ -373,14 +306,13 @@ 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
clampCursorX()
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
flush()
elseif e[3] == "key" then
if key(e[4], e[5], e[6]) then
if key(e[4] ~= 0 and unicode.char(e[4]), e[5], e[6]) then
flush()
end
elseif e[3] == "focus" then
focFlag = e[4] and neo.scheduleTimer(0)
ctrlFlag = false
elseif e[3] == "close" then
return
@ -392,7 +324,7 @@ while true do
if c == "\n" then
c = "\r"
end
putLetter(c)
key(c, 0, true)
end
end
flush()

491
code/apps/svc-t.lua Normal file
View File

@ -0,0 +1,491 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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

@ -600,65 +600,55 @@ end
local isAltDown = false
local isCtrDown = false
local function key(ku, ka, kc, down)
local ku = screens.getMonitorByKeyboard(ku)
if not ku then return end
local ku, lin = screens.getMonitorByKeyboard(ku)
for k, v in ipairs(monitors) do
if v[2] == mu then
lIM = k
if ku and v[2] == ku then
lin = k
break
end
end
if not lin then return end
lIM = lin
local focus = surfaces[1]
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
if kc == 200 then
if focus and down then ofsSurface(focus, 0, -1) end return
end
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 == 97 then
if not down then
isAltDown = false
end
return
end
if ka == 3 or ka == 99 then
if down then
if isCtrDown then
error("User-authorized Everest crash.")
else
if focus then
focus[6](focus[8], "close")
end
end
end
return
end
if ka == 13 then
if down then
startLauncher()
end
return
end
if kc == 29 then
isCtrDown = down
elseif kc == 56 then
isAltDown = down
end
if focus then
if isAltDown and ka == 122 then
if focus and down then
local n = table.remove(surfaces, 1)
table.insert(surfaces, n)
changeFocus(n)
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
end
elseif isAltDown and (ka == 3 or ka == 99) then
if down then
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()
end
elseif focus then
if kc ~= 56 then
lIM = focus[1]
end

View File

@ -81,20 +81,12 @@ local function getPfx(xd, pkg)
end
end
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
local function splitAC(ac)
local sb = ac:match("/[a-z0-9/%.]*$")
if sb then
return ac:sub(1, #ac - #sb), sb
end
return ac
end
donkonitDFProvider(function (pkg, pid, sendSig)
@ -130,7 +122,8 @@ donkonitDFProvider(function (pkg, pid, sendSig)
myApi = getPfx("", pkg),
lockPerm = function (perm)
-- Are we allowed to?
if not matchesSvc("x.", pkg, perm) then
local permPfx, detail = splitAC(perm)
if getPfx("x.", pkg) ~= permPfx then
return false, "You don't own this permission."
end
local set = "perm|*|" .. perm
@ -223,7 +216,11 @@ 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 ok, err = pcall(secpol, nexus, settings, proc.pkg, pid, perm, req, matchesSvc)
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))
if not ok then
neo.emergency("Used fallback policy because of run-err: " .. err)
req(def)
@ -243,11 +240,7 @@ 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 = perm:sub(7)
local paP = appAct:match(endAcPattern)
if paP then
appAct = appAct:sub(1, #appAct - #paP)
end
local appAct = splitAC(perm:sub(3))
-- Prepare for success
onReg[perm] = onReg[perm] or {}
local orp = onReg[perm]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -459,7 +459,7 @@ function retrieveAccess(perm, pkg, pid)
accesses[uid] = function (pkg, pid)
return nil
end
return function (f)
return function (f, secret)
-- Registration function
ensureType(f, "function")
local accessObjectCache = {}
@ -481,8 +481,10 @@ function retrieveAccess(perm, pkg, pid)
end
-- returns nil and fails
end
-- Announce registration
distEvent(nil, "k.registration", uid)
if not secret then
-- Announce registration
distEvent(nil, "k.registration", uid)
end
end, function ()
-- Registration becomes null (access is held but other processes cannot retrieve object)
if accesses[uid] then

73
code/libs/lineedit.lua Normal file
View File

@ -0,0 +1,73 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
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

@ -339,6 +339,7 @@ newNeoux = function (event, neo)
end
-- Note: w should be at least 2 - this is similar to buttons.
neoux.tcfield = function (x, y, w, textprop)
local p = 1
return {
x = x,
y = y,
@ -347,22 +348,24 @@ 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", textprop())
neo.requireAccess("x.neo.pub.globals", "clipboard").setSetting("clipboard", ot)
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 == 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
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
end
end
end,
@ -377,9 +380,10 @@ newNeoux = function (event, neo)
fg = bg
bg = fg1
end
local text = unicode.safeTextFormat(textprop())
text = "[" .. neoux.pad(text, w - 2, false, true, true) .. "]"
window.span(x, y, text, bg, fg)
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)
end
}
end

View File

@ -94,11 +94,25 @@ 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
if fsc.isDirectory(fp) then
local cDir = fsc.isDirectory(fp)
if cDir then
nm = "D: " .. v
end
n[k + 1] = {nm, function () return nil, getFsNode(fs, t, fsc, fp, mode, impliedName) 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
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
@ -151,24 +165,6 @@ function getFsNode(fs, parent, fsc, path, mode, impliedName)
end
end
if not dir then
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})
if (fscrw or mode == false) and (mode ~= nil) then
local tx = "Open"
if mode == true then
tx = "Save (Overwrite)"
elseif mode == "append" then
tx = "Append"
end
if fscrw or mode == false then
table.insert(n, {tx, selectUnknown})
end
end
elseif impliedName then
table.insert(n, {"Implied: " .. impliedName, function ()
return selectUnknown(impliedName)

View File

@ -11,13 +11,19 @@
-- IRC is usually pretty safe, but no guarantees.
-- Returns "allow", "deny", or "ask".
local function actualPolicy(pkg, pid, perm, matchesSvc)
local function actualPolicy(pkg, pid, perm, pkgSvcPfx)
-- 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 (aka Icecap) is open to all
-- x.neo.pub.* is open to all
if perm:sub(1, 10) == "x.neo.pub." then
return "allow"
end
@ -25,7 +31,8 @@ local function actualPolicy(pkg, pid, perm, matchesSvc)
if perm == "s.h.component_added" or perm == "s.h.component_removed" or perm == "s.h.tablet_use" or perm == "c.tablet" then
return "allow"
end
if matchesSvc("r.", pkg, perm) then
-- Userlevel can register for itself
if perm == "r." .. pkgSvcPfx then
return "allow"
end
-- Userlevel has no other registration rights
@ -44,8 +51,8 @@ local function actualPolicy(pkg, pid, perm, matchesSvc)
return "ask"
end
return function (nexus, settings, pkg, pid, perm, rsp, matchesSvc)
local res = actualPolicy(pkg, pid, perm, matchesSvc)
return function (nexus, settings, pkg, pid, perm, rsp, pkgSvcPfx)
local res = actualPolicy(pkg, pid, perm, pkgSvcPfx)
if settings then
res = settings.getSetting("perm|" .. pkg .. "|" .. perm) or
settings.getSetting("perm|*|" .. perm) or res

View File

@ -1,49 +0,0 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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.
io.write("\x00") -- initiation character
local blk = io.read("*a")
local windowSize = 0x10000
local windowData = ("\x00"):rep(windowSize)
local function crop(data)
windowData = (windowData .. data):sub(-windowSize)
end
while blk ~= "" do
local bestData = blk:sub(1, 1)
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
-- anti ']'-corruption helper
if thirdByte ~= 93 then
bestData = string.char(128 + lm, math.floor(pm / 256), thirdByte)
bestRes = pfx
end
end
-- ok, encode!
io.write(bestData)
crop(bestRes)
blk = blk:sub(#bestRes + 1)
end

View File

@ -1,88 +0,0 @@
-- 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"},
{"=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,72 +0,0 @@
-- This is released into the public domain. XX
-- No warranty is provided, implied or otherwise. XX
local sector = io.write -- XX
-- XX
-- BUNDIVIDE (r5 edition) reference implementation for integration XX
-- Lines ending with XX are not included in the output. XX
-- Lines that both start and end with -- are only for use in the output, XX
-- and are thus not executed during any sanity-check procedure. XX
-- XX
Cp,Ct,Cc,Cw="","","",("\x00"):rep(65536)
-- High-level breakdown: XX
-- CP is unescaper & TAR-sector-breakup. XX
-- It'll only begin to input if at least 3 bytes are available, XX
-- so you'll want to throw in 2 extra zeroes at the end of stream as done here. XX
-- It uses Ct (input buffer) and Cp (output buffer). XX
-- Ignore its second argument, as that's a lie, it's just there for a local. XX
-- CD is the actual decompressor. It has the same quirk as CP, wanting two more bytes. XX
-- It stores to Cc (compressed), and Cw (window). XX
-- It uses Ca as the "first null" activation flag. XX
-- It outputs that which goes to the window to CP also. XX
-- And it also uses a fake local. XX
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 > 255 then
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
-- XX
CD = function (d, b, p)
Cc = Cc .. d
while #Cc > 2 do
b = Cc:byte()
if not Ca then
Ca, b, Cc = b < 1, "", Cc:sub(2)
elseif b < 128 then
b, Cc = Cc:sub(1, 1), Cc:sub(2)
else
p = Cc:byte(2) * 256 + Cc:byte(3) + 1
b, Cc = Cw:sub(p, p + b - 125), Cc:sub(4)
end
CP(b)
Cw = (Cw .. b):sub(-65536)
end
end
-- XX
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--
-- XX
CD("\x00\x00")CP("\x00\x00")

View File

@ -1,20 +0,0 @@
-- 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,27 +0,0 @@
-- 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

36
inst/bdivide/bga.lua Normal file
View File

@ -0,0 +1,36 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
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

85
inst/bdivide/compress.lua Normal file
View File

@ -0,0 +1,85 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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

72
inst/bdivide/core.lua Normal file
View File

@ -0,0 +1,72 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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

64
inst/bdivide/instdeco.lua Normal file
View File

@ -0,0 +1,64 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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
$}

18
inst/bdvlite/compress.lua Normal file
View File

@ -0,0 +1,18 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
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

72
inst/bdvlite/core.lua Normal file
View File

@ -0,0 +1,72 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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

30
inst/bdvlite/instdeco.lua Normal file
View File

@ -0,0 +1,30 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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
$}

78
inst/build.lua Normal file
View File

@ -0,0 +1,78 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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()

14
inst/deflate/compress.lua Normal file
View File

@ -0,0 +1,14 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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

287
inst/deflate/instdeco.lua Normal file
View File

@ -0,0 +1,287 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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
$}

76
inst/instcore.lua Normal file
View File

@ -0,0 +1,76 @@
-- KOSNEO installer base
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
$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

67
inst/insthead.lua Normal file
View File

@ -0,0 +1,67 @@
-- KOSNEO installer base
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
$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

30
inst/libs/frw.lua Normal file
View File

@ -0,0 +1,30 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
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
}

191
inst/libs/lexcrunch.lua Normal file
View File

@ -0,0 +1,191 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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

36
inst/status.lua Normal file
View File

@ -0,0 +1,36 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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")

32
inst/symbolGuide.md Normal file
View File

@ -0,0 +1,32 @@
# 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

@ -0,0 +1,10 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- Example compression engine.
-- Given: data, lexCrunch
-- returns compressionEngine, compressedData
return function (data, lexCrunch)
return lexCrunch.process(" $engineInput = $engineOutput ", {}), data
end

View File

@ -1,97 +0,0 @@
-- 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

@ -3,8 +3,8 @@ ocemu {
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},
{"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},

View File

@ -7,5 +7,5 @@ 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
./package.sh $*
cp inst.lua laboratory/c1-sda/init.lua

View File

@ -1,28 +0,0 @@
-- 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()

View File

@ -9,15 +9,15 @@ 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"?
tar --mtime=0 --owner=gray:0 --group=mann:0 -cf code.tar code
cd code
tar --mtime=0 --owner=gray:0 --group=mann:0 -cf ../code.tar .
cd ..
# Solely for ensuring that a -gold.lua file can be checked before being pushed to repository.
echo -n "-- commit: " > inst.lua
git status --porcelain=2 --branch | grep branch.oid >> inst.lua
lua heroes.lua `wc -c code.tar` | lua com2/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

View File

@ -1,609 +0,0 @@
-- 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ñ³ƒÈ«ž˜=£
--]]

Binary file not shown.

View File

@ -1,181 +0,0 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- app-kmt.lua : LC emergency plan
-- Authors: 20kdc
local lcOverride = false
local l15 = "20kdc.duckdns.org:8888"
-- unicode.safeTextFormat'd lines
local console = {
"┎┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┒",
"┋ ┃ ┃╲ ╱┃ ▀▀┃▀▀ ┋",
"┋ ┃╳ ┃ ╲ ┃ ┃ ┋",
"┋ ┃ ╲ ┃ ╲╱ ┃ ┃ ┋",
"┋ ┋",
"┋ KittenOS NEO MUD Terminal ┋",
"┖┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┚",
":Type text, Enter key sends.",
":'>' is 'from you', '<' 'to you'.",
":':' is 'internal message'.",
":Control-Arrows resizes window.",
-- ":F5/F6 copies/pastes current line.",
":",
":",
":Enter target server:port",
-- 14
}
--++++++++++++++++++++++++++++++++++++++++
-- sW must not go below 3.
-- sH must not go below 2.
local sW, sH = 40, 15
local inet = neo.requireAccess("c.internet", "internet").list()()
local windows = neo.requireAccess("x.neo.pub.window", "windows")
local window = windows(sW, sH)
local tcp = nil
local dummyTcp = {read = function() return "" end, write = function() end, close = function() end}
local tcpBuf = ""
local function fmtLine(s)
s = unicode.safeTextFormat(s)
local l = unicode.len(s)
return unicode.sub(s .. (" "):rep(sW - l), -sW)
end
local function line(i)
if i ~= sH then
assert(console[i], "console" .. i)
window.span(1, i, fmtLine(console[i]), 0xFFFFFF, 0)
else
window.span(1, i, fmtLine(l15), 0, 0xFFFFFF)
end
end
local function incoming(s)
local function shift(f)
for i = 1, #console - 1 do
console[i] = console[i + 1]
end
console[#console] = f
end
-- Need to break this safely.
shift("")
for i = 1, unicode.len(s) do
local ch = unicode.sub(s, i, i)
if unicode.wlen(console[#console] .. ch) > sW then
shift(" ")
end
console[#console] = console[#console] .. ch
end
for i = 1, #console do
line(i)
end
end
local function submitLine()
incoming(">" .. l15)
if not tcp then
tcp = inet.connect(l15)
if not tcp then
incoming(":The connection could not be created.")
tcp = dummyTcp
else
if not tcp.finishConnect() then
incoming(":Warning: finishConnect = false")
end
neo.scheduleTimer(os.uptime() + 0.1)
end
else
-- PRJblackstar doesn't need \r but others might
tcp.write(l15 .. "\r\n")
end
l15 = ""
line(sH)
end
if lcOverride then
submitLine()
end
local function clipEnt(tx)
tx = tx:gsub("\r", "")
local ci = tx:find("\n") or (#tx + 1)
tx = tx:sub(1, ci - 1)
l15 = tx
line(sH)
end
local control = false
while true do
local e = {coroutine.yield()}
if e[1] == "k.timer" then
while true do
local b, e = tcp.read(neo.readBufSize)
if not b then
if e then
incoming(":Warning: " .. e)
tcp.close()
tcp = dummyTcp
end
break
elseif b == "" then
break
else
tcpBuf = tcpBuf .. b:gsub("\r", "")
while true do
local nlp = tcpBuf:find("\n")
if nlp then
incoming("<" .. tcpBuf:sub(1, nlp - 1))
tcpBuf = tcpBuf:sub(nlp + 1)
else
break
end
end
end
end
neo.scheduleTimer(os.uptime() + 0.1)
elseif e[1] == "x.neo.pub.window" then
if e[3] == "close" then
if tcp then
tcp.close()
end
return
elseif e[3] == "clipboard" then
clipEnt(e[4])
elseif e[3] == "key" then
if e[5] == 29 or e[5] == 157 then
control = e[6]
elseif e[6] then
if not control then
if e[4] == 8 or e[4] == 127 then
l15 = unicode.sub(l15, 1, -2)
elseif e[4] == 13 then
submitLine()
elseif e[4] >= 32 then
l15 = l15 .. unicode.char(e[4])
end
line(sH)
elseif e[5] == 203 and sW > 8 then
sW = sW - 1
window.setSize(sW, sH)
elseif e[5] == 200 and sH > 2 then
sH = sH - 1
table.remove(console, 1)
window.setSize(sW, sH)
elseif e[5] == 205 then
sW = sW + 1
window.setSize(sW, sH)
elseif e[5] == 208 then
sH = sH + 1
table.insert(console, 1, "")
window.setSize(sW, sH)
end
end
elseif e[3] == "line" then
line(e[4])
end
end
end

View File

@ -0,0 +1,96 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- 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

@ -63,7 +63,8 @@ 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))
function (function (pkg, pid, send),
secret)
While the registration is locked on
success, attempting to use it will
fail, as no handler has been given.
@ -71,6 +72,11 @@ 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,2 +0,0 @@
repository/apps/app-kmt.lua: 20kdc, Public Domain

View File

@ -0,0 +1,2 @@
repository/apps/app-telnet.lua: 20kdc, Public Domain

View File

@ -16,4 +16,6 @@ repository/docs/us-nxapp: 20kdc, Public Domain
repository/docs/us-perms: 20kdc, Public Domain
repository/docs/us-setti: 20kdc, Public Domain
repository/docs/us-clawf: 20kdc, Public Domain
repository/docs/ul-linee: 20kdc, Public Domain
repository/docs/us-termi: 20kdc, Public Domain

View File

@ -1,3 +1,4 @@
repository/apps/svc-virtudev.lua: 20kdc, Public Domain
repository/apps/app-vdrslamp.lua: 20kdc, Public Domain
repository/docs/us-virtu: 20kdc, Public Domain

78
repository/docs/ul-linee Normal file
View File

@ -0,0 +1,78 @@
The "lineedit" library provides a
quick way to implement nice line
editing into applications.
It requires the user to store the
line and the cursor position for the
line, which allows the user to
implement any additional logic for
the application.
(To clarify, this implies that the
library's functions are stateless.)
The functions are as follows:
draw = function (sW, line, curX, rX):
Returns the spantext.
sW is the width of the area.
line is the safeTextFormatted text.
curX is the cursor in screen units
(usually done as part of the text
formatting)
This is optional, and if not provided
the cursor will not be shown.
rX is an optional camera override.
If not provided, the cursor will be
used; if that is not provided, then
the left side will be shown.
clamp = function (line, curX):
Returns curX clamped to the line's
length (does not check for < 1).
key = function (ks, kc, line, curX):
Performs something.
ks, if truthy, is the unicode.char
of the key event key character.
kc is the key event keycode.
(If feeding in clipboard characters,
use 0 here.)
line is the text.
This returns three values, any of
which or all of which may be nil for
a 'no effect' response:
The new line text, 'lT'.
The new curX, 'lC'.
The extended action, 'lX'.
The following extended actions exist:
"l<": The cursor should be warped to
the end of the previous line, if one
exists (if not, do nothing)
"l>": The cursor should be warped to
the start of the next line, if one
exists (if not, do nothing)
"w<": This line should be welded to
the previous line, and the cursor
should be placed at the weld point.
"w>": This line should be welded to
the next line, and the cursor
should be placed at the weld point.
"nl": The Enter button was pressed.
-- This is released into
the public domain.
-- No warranty is provided,
implied or otherwise.

114
repository/docs/us-termi Normal file
View File

@ -0,0 +1,114 @@
The "svc-t" program / "x.neo.pub.t"
permission makes up the terminal
subsystem for KittenOS NEO.
--- THEORETICAL TERMINALS MODEL ---
The theoretical model for terminals
in KittenOS NEO is a TELNET client,
using the non-standard behavior of
treating a lack of remote echo as
meaning 'local line editing'.
To prevent code size going too far,
the shipped terminal supports:
1. Built-in history as part of the
line editing functionality
2. ANSI.SYS-compatible display, but
no support for attributes.
If you really want full support,
write a better terminal application.
A process starting another process
connected to the same terminal is
advised to wait for that process to
die before continuing reading input.
The controlling process is whichever
process is supposed to be accepting
user input. This is contextual, and
there is no mechanism to control
this explicitly.
The controlling process should show
text in response to any user input,
or at least provide some form of
acknowledgement that user input has
been received.
For convenience, terminal echo is on
by default; this is easily remedied.
--- ACTUAL USAGE OF TERMINALS ---
Access control on terminals is looser
than for most permissions, as it has
to be able to be 'sublet' in some
cases, including events.
As such, the secret flag is set for
terminal registration.
A terminal program is given a string
argument for the ID of the terminal
to connect to.
A terminal always has an ID beginning
with "x.neo.pub.t/". ALWAYS CHECK.
Requiring the responsible access
connects to the terminal. All
terminal programs SHOULD check for
the death of their parent terminal
(via the k.procdie event) and
self-destruct accordingly.
A program may start svc-t directly.
In this case, it must pass a function
(resTbl) and may pass a title.
When the terminal has shown, the
function provided is called with a
table as follows:
access = "x.neo.pub.t/<...>"
close = function (): close terminal
The k.kill permission and the close
function are the only ways for a
program to kill a terminal, and the
close function is only given to the
creating process.
In either case, when the access has
been acquired, the following API is
presented:
id = "x.neo.pub.t/<...>"
pid = <The terminal's PID.>
write = function (text): Writes the
TELNET data to the terminal.
User input is provided in events:
<id>, "data", <data>
TELNET commands are provided in:
<id>, "telnet", <data>
There is a total of one TELNET
command per event, unpadded.
Notably, intermixing the data part
of the data/telnet events in order
produces the full terminal-to-server
TELNET stream.
-- This is released into
the public domain.
-- No warranty is provided,
implied or otherwise.

26
repository/docs/us-virtu Normal file
View File

@ -0,0 +1,26 @@
The "x.svc.virtudev" service allows
the creation of virtual components.
It provides two functions:
install = function (proxy):
Installs a virtual component.
The provided proxy must have:
address: The address (string)
type: The type (string)
It SHOULD have the 'slot' attribute
that proxies have.
uninstall = function (address):
Uninstalls a component that your
process installed by address.
The service automatically starts
and stops as required.
-- This is released into
the public domain.
-- No warranty is provided,
implied or otherwise.