From d25b780d1fb0cefad3c2054f06d4b5de025b13f5 Mon Sep 17 00:00:00 2001 From: gamax92 Date: Sat, 25 Apr 2015 18:41:03 -0600 Subject: [PATCH] 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 --- list.lua | 11 ++- src/apis/component.lua | 7 +- src/apis/computer.lua | 22 ++++-- src/apis/unicode.lua | 70 ++++++++----------- src/boot.lua | 4 +- src/component/computer.lua | 9 ++- src/component/eeprom.lua | 31 ++++++-- src/component/filesystem.lua | 45 ++++++++++-- src/component/gpu.lua | 23 +++++- .../{keyboard.lua => keyboard_sdl2.lua} | 4 +- src/component/{screen.lua => screen_sdl2.lua} | 16 ++++- src/main.lua | 8 ++- src/support/crc32.lua | 17 +++++ src/{ => support}/sdl_to_lwjgl.lua | 0 14 files changed, 198 insertions(+), 69 deletions(-) rename src/component/{keyboard.lua => keyboard_sdl2.lua} (93%) rename src/component/{screen.lua => screen_sdl2.lua} (90%) create mode 100644 src/support/crc32.lua rename src/{ => support}/sdl_to_lwjgl.lua (100%) diff --git a/list.lua b/list.lua index fb2d995..249f7db 100644 --- a/list.lua +++ b/list.lua @@ -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() diff --git a/src/apis/component.lua b/src/apis/component.lua index a1dbd6e..f7adc13 100644 --- a/src/apis/component.lua +++ b/src/apis/component.lua @@ -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 diff --git a/src/apis/computer.lua b/src/apis/computer.lua index abd156f..fdcb8fb 100644 --- a/src/apis/computer.lua +++ b/src/apis/computer.lua @@ -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 diff --git a/src/apis/unicode.lua b/src/apis/unicode.lua index f74a9eb..42bb290 100644 --- a/src/apis/unicode.lua +++ b/src/apis/unicode.lua @@ -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 diff --git a/src/boot.lua b/src/boot.lua index e36036c..d915d53 100755 --- a/src/boot.lua +++ b/src/boot.lua @@ -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 diff --git a/src/component/computer.lua b/src/component/computer.lua index 7e24a08..41d996c 100644 --- a/src/component/computer.lua +++ b/src/component/computer.lua @@ -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 diff --git a/src/component/eeprom.lua b/src/component/eeprom.lua index a5ce247..c9e00a2 100644 --- a/src/component/eeprom.lua +++ b/src/component/eeprom.lua @@ -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 diff --git a/src/component/filesystem.lua b/src/component/filesystem.lua index 334c654..38c0d81 100644 --- a/src/component/filesystem.lua +++ b/src/component/filesystem.lua @@ -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 diff --git a/src/component/gpu.lua b/src/component/gpu.lua index 26a0ee2..325d2ed 100644 --- a/src/component/gpu.lua +++ b/src/component/gpu.lua @@ -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 diff --git a/src/component/keyboard.lua b/src/component/keyboard_sdl2.lua similarity index 93% rename from src/component/keyboard.lua rename to src/component/keyboard_sdl2.lua index 48c9644..27d240e 100644 --- a/src/component/keyboard.lua +++ b/src/component/keyboard_sdl2.lua @@ -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 = {} diff --git a/src/component/screen.lua b/src/component/screen_sdl2.lua similarity index 90% rename from src/component/screen.lua rename to src/component/screen_sdl2.lua index e006504..a2f663c 100644 --- a/src/component/screen.lua +++ b/src/component/screen_sdl2.lua @@ -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 diff --git a/src/main.lua b/src/main.lua index dd54a58..28e5e12 100644 --- a/src/main.lua +++ b/src/main.lua @@ -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(), diff --git a/src/support/crc32.lua b/src/support/crc32.lua new file mode 100644 index 0000000..2a9f44f --- /dev/null +++ b/src/support/crc32.lua @@ -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 + diff --git a/src/sdl_to_lwjgl.lua b/src/support/sdl_to_lwjgl.lua similarity index 100% rename from src/sdl_to_lwjgl.lua rename to src/support/sdl_to_lwjgl.lua