Bunch of updates

Have component stub generator also generate documentation
Add documentation to the components
Support components defining their own type
add return nil, "no such component" to more component functions
fix component.doc
Add argument checks to computer api, maybe implement computer.realTime?
Implement unicode isWide, charWidth, and wlen
clean up boot.lua a little
Implement readonly mode in eeprom, and implement crc32 checksum
Add more readonly checks to filesystem, implement filesystem.rename
makeDirectory returns false if the path exists
suffix keyboard and screen with _sdl, and check for SDL before loading
move sdl_to_lwjgl to the support folder
This commit is contained in:
gamax92 2015-04-25 18:41:03 -06:00
parent 0004498294
commit d25b780d1f
14 changed files with 198 additions and 69 deletions

View File

@ -5,6 +5,7 @@ end
local component = require("component")
local address = component.get(args[1])
local proxy = component.proxy(address)
print(proxy.type)
local keys = {}
for k,v in pairs(proxy) do
if type(v) == "table" then
@ -25,5 +26,13 @@ for i = 1,#keys do
end
file:write("function obj." .. k .. "(" .. doc .. ") " .. comment .."\n\t--STUB\n\tcprint(\"" .. proxy.type .. "." .. k .. "\"" .. (doc ~= "" and "," or "") .. doc .. ")\nend\n")
end
file:write("\nlocal cec = {}\n\nreturn obj,cec")
file:write("\nlocal cec = {}\n\nlocal doc = {\n")
for i = 1,#keys do
local k = keys[i]
if component.doc(address,k) ~= nil then
local doc = component.doc(address,k)
file:write(string.format("\t[%q]=%q,\n",k,doc))
end
end
file:write("}\n\nreturn obj,cec,doc")
file:close()

View File

@ -32,7 +32,7 @@ for k,v in pairs(components) do
end
local proxy, cec, doc = fn(table.unpack(v,2))
proxy.address = address
proxy.type = v[1]
proxy.type = proxy.type or v[1]
proxylist[address] = proxy
emuicc[address] = cec
doclist[address] = doc
@ -115,6 +115,7 @@ function env.component.methods(address)
end
return methods
end
return nil, "no such component"
end
function env.component.invoke(address, method, ...)
@ -126,6 +127,7 @@ function env.component.invoke(address, method, ...)
end
return true, proxylist[address][method](...)
end
return nil, "no such component"
end
function env.component.doc(address, method)
@ -133,11 +135,12 @@ function env.component.doc(address, method)
checkArg(2,method,"string")
if proxylist[address] ~= nil then
if proxylist[address][method] == nil then
error("no such method",2)
return nil
end
if doclist[address] ~= nil then
return doclist[address][method]
end
return nil
end
return nil, "no such component"
end

View File

@ -1,5 +1,13 @@
local env = ...
local sok,socket = pcall(require,"socket")
local gettime
if sok then
gettime = socket.gettime
else
gettime = os.time
end
local tmpaddr = "tmp-address"
computer = {}
@ -11,12 +19,12 @@ end
env.computer = {}
function env.computer.realTime()
--STUB
--TODO
--cprint("computer.realTime") -- Spammy
return 0
return gettime()
end
function env.computer.uptime()
--STUB
--TODO
cprint("computer.uptime")
return elsa.timer.getTime() - machine.starttime
end
@ -35,8 +43,8 @@ function env.computer.totalMemory()
return 10000
end
function env.computer.pushSignal(name, ...)
--TODO
cprint("computer.pushSignal", name, ...)
compCheckArg(1,name,"string")
table.insert(machine.signals, {name, ... })
end
function env.computer.tmpAddress()
@ -51,11 +59,13 @@ end
function env.computer.addUser(username)
--STUB
cprint("computer.addUser", username)
compCheckArg(1,username,"string")
return nil, "player must be online"
end
function env.computer.removeUser(username)
--STUB
cprint("computer.removeUser", username)
compCheckArg(1,username,"string")
return false
end
function env.computer.energy()
@ -64,7 +74,7 @@ function env.computer.energy()
return math.huge
end
function env.computer.maxEnergy()
--STUB
--STUB, move to a config
cprint("computer.maxEnergy")
return math.huge
return 500
end

View File

@ -2,59 +2,51 @@ local env = ...
local utf8 = require("utf8")
env.unicode = {}
env.unicode = {
lower = utf8.lower,
upper = utf8.upper,
char = utf8.char,
len = utf8.len,
reverse = utf8.reverse,
sub = utf8.sub,
}
function env.unicode.lower(str)
-- STUB
cprint("unicode.lower", str)
checkArg(1,str,"string")
return utf8.lower(str)
end
function env.unicode.upper(str)
-- STUB
cprint("unicode.upper", str)
checkArg(1,str,"string")
return utf8.upper(str)
end
function env.unicode.char(...)
-- TODO
cprint("unicode.char", ...)
return utf8.char(...)
end
function env.unicode.len(str)
-- TODO
cprint("unicode.len", str)
checkArg(1,str,"string")
return utf8.len(str)
end
function env.unicode.reverse(str)
-- TODO
cprint("unicode.reverse", str)
checkArg(1,str,"string")
return utf8.reverse(str)
end
function env.unicode.sub(str, i, j)
-- TODO
cprint("unicode.sub", str, i, j)
return utf8.sub(str, i, j)
end
function env.unicode.isWide(str)
-- STUB
cprint("unicode.isWide", str)
checkArg(1,str,"string")
if #str == 0 then
error("String index out of range: 0",3)
end
local char = utf8.byte(str)
if unifont[char] ~= nil then
return #unifont[char] > 32
end
return false
end
function env.unicode.charWidth(str)
-- STUB
cprint("unicode.charWidth", str)
checkArg(1,str,"string")
if #str == 0 then
error("String index out of range: 0",3)
end
local char = utf8.byte(str)
if unifont[char] ~= nil then
return #unifont[char] / 32
end
return 1
end
function env.unicode.wlen(str)
-- STUB
cprint("unicode.wlen", str)
checkArg(1,str,"string")
return utf8.len(str)
local length = 0
for _,c in utf8.next, str do
if unifont[c] ~= nil then
length = length + #unifont[c] / 32
else
length = length + 1
end
end
return length
end
function env.unicode.wtrunc(str, count)
-- STUB

View File

@ -122,7 +122,7 @@ local function boot()
SDL = SDL,
windowEventID = wen,
}
require("main")
local e = ffi.new('SDL_Event')
@ -145,7 +145,7 @@ local function boot()
if elsa.draw then
elsa.draw()
end
SDL.delay(1)
end
end

View File

@ -28,4 +28,11 @@ end
local cec = {}
return obj,cec
local doc = {
["isRunning"]="function():boolean -- Returns whether the computer is running.",
["beep"]="function([frequency:number[, duration:number]]) -- Plays a tone, useful to alert users via audible feedback.",
["stop"]="function():boolean -- Stops the computer. Returns true if the state changed.",
["start"]="function():boolean -- Starts the computer. Returns true if the state changed.",
}
return obj,cec,doc

View File

@ -1,8 +1,11 @@
local address, slot, filename = ...
local crc32 = require("support.crc32")
local code = elsa.filesystem.read(filename)
local data = ""
local label = "EEPROM"
local readonly = false
-- eeprom component
local obj = {}
@ -34,15 +37,17 @@ function obj.getLabel() -- Get the label of the EEPROM.
end
function obj.setLabel(newlabel) -- Set the label of the EEPROM.
cprint("eeprom.setLabel", newlabel)
if readonly then
return nil, "storage is readonly"
end
compCheckArg(1,newlabel,"string","nil")
if newlabel == nil then newlabel = "EEPROM" end
label = newlabel:sub(1,16)
return label
end
function obj.getChecksum() -- Get the checksum of the data on this EEPROM.
-- STUB
cprint("eeprom.getChecksum")
return "1badbabe"
return string.format("%08x", tonumber(crc32(code)))
end
function obj.get() -- Get the currently stored byte array.
cprint("eeprom.get")
@ -50,6 +55,9 @@ function obj.get() -- Get the currently stored byte array.
end
function obj.set(newcode) -- Overwrite the currently stored byte array.
cprint("eeprom.set", newcode)
if readonly then
return nil, "storage is readonly"
end
compCheckArg(1,newcode,"string","nil")
if newcode == nil then newcode = "" end
if #newcode > 4096 then
@ -58,15 +66,28 @@ function obj.set(newcode) -- Overwrite the currently stored byte array.
code = newcode
end
function obj.makeReadonly(checksum) -- Make this EEPROM readonly if it isn't already. This process cannot be reversed!
--STUB
print("eeprom.makeReadonly", checksum)
compCheckArg(1,checksum,"string")
if checksum ~= obj.getChecksum() then
return nil, "incorrect checksum"
end
return false
readonly = true
return true
end
local cec = {}
return obj,cec
local doc = {
["getData"]="function():string -- Get the currently stored byte array.",
["setData"]="function(data:string) -- Overwrite the currently stored byte array.",
["getDataSize"]="function():string -- Get the storage capacity of this EEPROM.",
["getSize"]="function():string -- Get the storage capacity of this EEPROM.",
["getLabel"]="function():string -- Get the label of the EEPROM.",
["setLabel"]="function(data:string):string -- Set the label of the EEPROM.",
["getChecksum"]="function():string -- Get the checksum of the data on this EEPROM.",
["get"]="function():string -- Get the currently stored byte array.",
["set"]="function(data:string) -- Overwrite the currently stored byte array.",
["makeReadonly"]="function(checksum:string):boolean -- Make this EEPROM readonly if it isn't already. This process cannot be reversed!",
}
return obj,cec,doc

View File

@ -68,7 +68,6 @@ function obj.spaceUsed() -- The currently used capacity of the file system, in b
return 0
end
function obj.rename(from, to) -- Renames/moves an object from the first specified absolute path in the file system to the second.
--STUB
cprint("filesystem.rename", from, to)
compCheckArg(1,from,"string")
from = cleanPath(from)
@ -80,7 +79,10 @@ function obj.rename(from, to) -- Renames/moves an object from the first specifie
if to == ".." or to:sub(1,3) == "../" then
return nil,"file not found"
end
return false
if readonly then
return false
end
return os.rename(directory .. "/" .. from, directory .. "/" .. to) == true
end
function obj.close(handle) -- Closes an open file descriptor with the specified handle.
cprint("filesystem.close", handle)
@ -108,6 +110,9 @@ function obj.remove(path) -- Removes the object at the specified absolute path i
if path == ".." or path:sub(1,3) == "../" then
return nil,"file not found"
end
if readonly then
return false
end
return elsa.filesystem.remove(directory .. "/" .. path)
end
function obj.size(path) -- Returns the size of the object at the specified absolute path in the file system.
@ -140,13 +145,15 @@ function obj.getLabel() -- Get the current label of the file system.
return label
end
function obj.setLabel(value) -- Sets the label of the file system. Returns the new value, which may be truncated.
--TODO
--TODO: treat functions as nil
cprint("filesystem.setLabel", value)
compCheckArg(1,value,"string")
if readonly then
error("label is read only",3)
end
label = value:sub(1,16)
end
function obj.open(path, mode) -- Opens a new file descriptor and returns its handle.
--TODO
cprint("filesystem.open", path, mode)
if mode == nil then mode = "r" end
compCheckArg(1,path,"string")
@ -158,6 +165,9 @@ function obj.open(path, mode) -- Opens a new file descriptor and returns its han
if mode ~= "r" and mode ~= "rb" and mode ~= "w" and mode ~= "wb" and mode ~= "a" and mode ~= "ab" then
error("unsupported mode",3)
end
if (not (mode == "r" or mode == "rb") and readonly) or ((mode == "r" or mode == "rb") and not elsa.filesystem.exists(directory .. "/" .. path)) then
return nil,"file not found"
end
local file, err = elsa.filesystem.newFile(directory .. "/" .. path, mode:sub(1,1))
if not file then return nil, err end
while true do
@ -204,13 +214,15 @@ function obj.isReadOnly() -- Returns whether the file system is read-only.
return readonly
end
function obj.makeDirectory(path) -- Creates a directory at the specified absolute path in the file system. Creates parent directories, if necessary.
--TODO
cprint("filesystem.makeDirectory", path)
compCheckArg(1,path,"string")
path = cleanPath(path)
if path == ".." or path:sub(1,3) == "../" then
return nil,"file not found"
end
if elsa.filesystem.exists(directory .. "/" .. path) or readonly then
return false
end
return elsa.filesystem.createDirectory(directory .. "/" .. path)
end
function obj.isDirectory(path) -- Returns whether the object at the specified absolute path in the file system is a directory.
@ -225,4 +237,25 @@ end
local cec = {}
return obj,cec
local doc = {
["read"]="function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.",
["lastModified"]="function(path:string):number -- Returns the (real world) timestamp of when the object at the specified absolute path in the file system was modified.",
["spaceUsed"]="function():number -- The currently used capacity of the file system, in bytes.",
["rename"]="function(from:string, to:string):boolean -- Renames/moves an object from the first specified absolute path in the file system to the second.",
["close"]="function(handle:number) -- Closes an open file descriptor with the specified handle.",
["write"]="function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.",
["remove"]="function(path:string):boolean -- Removes the object at the specified absolute path in the file system.",
["size"]="function(path:string):number -- Returns the size of the object at the specified absolute path in the file system.",
["seek"]="function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.",
["spaceTotal"]="function():number -- The overall capacity of the file system, in bytes.",
["getLabel"]="function():string -- Get the current label of the file system.",
["setLabel"]="function(value:string):string -- Sets the label of the file system. Returns the new value, which may be truncated.",
["open"]="function(path:string[, mode:string='r']):number -- Opens a new file descriptor and returns its handle.",
["exists"]="function(path:string):boolean -- Returns whether an object exists at the specified absolute path in the file system.",
["list"]="function(path:string):table -- Returns a list of names of objects in the directory at the specified absolute path in the file system.",
["isReadOnly"]="function():boolean -- Returns whether the file system is read-only.",
["makeDirectory"]="function(path:string):boolean -- Creates a directory at the specified absolute path in the file system. Creates parent directories, if necessary.",
["isDirectory"]="function(path:string):boolean -- Returns whether the object at the specified absolute path in the file system is a directory.",
}
return obj,cec,doc

View File

@ -199,4 +199,25 @@ end
local cec = {}
return obj,cec
local doc = {
["bind"]="function(address:string):boolean -- Binds the GPU to the screen with the specified address.",
["getForeground"]="function():number, boolean -- Get the current foreground color and whether it's from the palette or not.",
["setForeground"]="function(value:number[, palette:boolean]):number, number or nil -- Sets the foreground color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.",
["getBackground"]="function():number, boolean -- Get the current background color and whether it's from the palette or not.",
["setBackground"]="function(value:number[, palette:boolean]):number, number or nil -- Sets the background color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.",
["getDepth"]="function():number -- Returns the currently set color depth.",
["setDepth"]="function(depth:number):number -- Set the color depth. Returns the previous value.",
["maxDepth"]="function():number -- Get the maximum supported color depth.",
["fill"]="function(x:number, y:number, width:number, height:number, char:string):boolean -- Fills a portion of the screen at the specified position with the specified size with the specified character.",
["getScreen"]="function():string -- Get the address of the screen the GPU is currently bound to.",
["getResolution"]="function():number, number -- Get the current screen resolution.",
["setResolution"]="function(width:number, height:number):boolean -- Set the screen resolution. Returns true if the resolution changed.",
["maxResolution"]="function():number, number -- Get the maximum screen resolution.",
["getPaletteColor"]="function(index:number):number -- Get the palette color at the specified palette index.",
["setPaletteColor"]="function(index:number, color:number):number -- Set the palette color at the specified palette index. Returns the previous value.",
["get"]="function(x:number, y:number):string, number, number, number or nil, number or nil -- Get the value displayed on the screen at the specified index, as well as the foreground and background color. If the foreground or background is from the palette, returns the palette indices as fourth and fifth results, else nil, respectively.",
["set"]="function(x:number, y:number, value:string[, vertical:boolean]):boolean -- Plots a string value to the screen at the specified position. Optionally writes the string vertically.",
["copy"]="function(x:number, y:number, width:number, height:number, tx:number, ty:number):boolean -- Copies a portion of the screen from the specified location with the specified size by the specified translation.",
}
return obj,cec,doc

View File

@ -4,7 +4,7 @@ local ffi = require("ffi")
local lua_utf8 = require("utf8")
-- Conversion table for SDL2 keys to LWJGL key codes
local keys = elsa.filesystem.load("sdl_to_lwjgl.lua")()
local keys = require("support.sdl_to_lwjgl")
local code2char = {}
@ -33,7 +33,7 @@ end
-- keyboard component
-- Much complex
local obj = {}
local obj = {type="keyboard"}
-- Such methods
local cec = {}

View File

@ -108,7 +108,7 @@ local touchinvert = false
local precise = false
-- screen component
local obj = {}
local obj = {type="screen"}
function obj.isTouchModeInverted() -- Whether touch mode is inverted (sneak-activate opens GUI, instead of normal activate).
cprint("screen.isTouchModeInverted")
@ -303,4 +303,16 @@ function cec.copy(x1, y1, w, h, tx, ty) -- Copies a portion of the screen from t
texture,copytexture=copytexture,texture
end
return obj,cec
local doc = {
["isTouchModeInverted"]="function():boolean -- Whether touch mode is inverted (sneak-activate opens GUI, instead of normal activate).",
["setTouchModeInverted"]="function(value:boolean):boolean -- Sets whether to invert touch mode (sneak-activate opens GUI, instead of normal activate).",
["isPrecise"]="function():boolean -- Returns whether the screen is in high precision mode (sub-pixel mouse event positions).",
["setPrecise"]="function(enabled:boolean):boolean -- Set whether to use high precision mode (sub-pixel mouse event positions).",
["turnOff"]="function():boolean -- Turns off the screen. Returns true if it was on.",
["turnOn"]="function():boolean -- Turns the screen on. Returns true if it was off.",
["isOn"]="function():boolean -- Returns whether the screen is currently on.",
["getAspectRatio"]="function():number, number -- The aspect ratio of the screen. For multi-block screens this is the number of blocks, horizontal and vertical.",
["getKeyboards"]="function():table -- The list of keyboards attached to the screen.",
}
return obj,cec,doc

View File

@ -8,15 +8,19 @@ conf = {
-- Read component files for parameter documentation
components = {
{"gpu",nil,0,160,50,3},
{"screen",nil,nil,80,25,3},
{"eeprom",nil,9,"lua/bios.lua"},
{"filesystem",nil,5,"loot/OpenOS",true},
{"filesystem",nil,nil,"tmpfs",false},
{"filesystem",nil,nil,nil,false},
{"computer"},
{"keyboard"},
}
}
if elsa.SDL then
table.insert(conf.components, {"screen_sdl2",nil,nil,80,25,3})
table.insert(conf.components, {"keyboard_sdl2"})
else
-- TODO: Alternatives
end
machine = {
starttime = elsa.timer.getTime(),

17
src/support/crc32.lua Normal file
View File

@ -0,0 +1,17 @@
local ffi = require("ffi")
local bit32 = require("bit32")
local s_crc32 = ffi.new('const uint32_t[16]', 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c)
return function(str)
local crc = 0
local len = #str
str = ffi.cast('const uint8_t*', str)
crc = bit32.bnot(crc)
for i = 0, len-1 do
crc = bit32.bxor(bit32.rshift(crc, 4), s_crc32[bit32.bxor(bit32.band(crc, 0xF), bit32.band(str[i], 0xF))])
crc = bit32.bxor(bit32.rshift(crc, 4), s_crc32[bit32.bxor(bit32.band(crc, 0xF), bit32.rshift(str[i], 4))])
end
return bit32.bnot(crc)
end