diff --git a/common/edit.lua b/common/edit.lua index 2dbd1ff..2a6e110 100644 --- a/common/edit.lua +++ b/common/edit.lua @@ -901,11 +901,7 @@ actions = { if fs.isReadOnly(filename) then actions.error("access denied") else - local s, m = pcall(function() - if not Util.writeLines(filename, tLines) then - error("Failed to open " .. filename) - end - end) + local s, m = pcall(Util.writeFile, filename, table.concat(tLines, '\n')) if s then lastSave = undo.chain[#undo.chain] diff --git a/compress/.package b/compress/.package new file mode 100644 index 0000000..b6e67db --- /dev/null +++ b/compress/.package @@ -0,0 +1,6 @@ +{ + title = 'Compress', + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/compress', + description = [[untar / gunzip]], + license = 'MIT', +} diff --git a/compress/apis/deflatelua.lua b/compress/apis/deflatelua.lua new file mode 100644 index 0000000..cf7c95e --- /dev/null +++ b/compress/apis/deflatelua.lua @@ -0,0 +1,530 @@ +--[[ +see: https://github.com/davidm/lua-compress-deflatelua/ +for licensing / details +--]] + +local M = {_TYPE='module', _NAME='compress.deflatelua', _VERSION='0.3.20111128'} + +local assert = assert +local error = error +local ipairs = ipairs +local pairs = pairs +local tostring = tostring +local type = type +local setmetatable = setmetatable +local io = io +local math = math +local table_sort = table.sort +local math_max = math.max +local string_char = string.char +local band = bit32.band +local lshift = bit32.lshift +local rshift = bit32.rshift + +local function runtime_error(s, level) + level = level or 1 + error(s, level+1) +end + + +local function make_outstate(outbs) + local outstate = {} + outstate.outbs = outbs + outstate.window = {} + outstate.window_pos = 1 + return outstate +end + + +local function output(outstate, byte) + local window_pos = outstate.window_pos + outstate.outbs(byte) + outstate.window[window_pos] = byte + outstate.window_pos = window_pos % 32768 + 1 -- 32K +end + + +local function noeof(val) + return assert(val, 'unexpected end of file') +end + + +local function hasbit(bits, bit) + return bits % (bit + bit) >= bit +end + + +local function memoize(f) + local mt = {} + local t = setmetatable({}, mt) + function mt:__index(k) + local v = f(k) + t[k] = v + return v + end + return t +end + + +-- small optimization (lookup table for powers of 2) +local pow2 = memoize(function(n) return 2^n end) + +--local tbits = memoize( +-- function(bits) +-- return memoize( function(bit) return getbit(bits, bit) end ) +-- end ) + + +-- weak metatable marking objects as bitstream type +local is_bitstream = setmetatable({}, {__mode='k'}) + +local function bytestream_from_file(fh) + local o = {} + function o.read() + local sb = fh:read(1) + if sb then return sb:byte() end + end + return o +end + + +local function bytestream_from_string(s) + local i = 1 + local o = {} + function o.read() + local by + if i <= #s then + by = s:byte(i) + i = i + 1 + end + return by + end + return o +end + + +local function bytestream_from_function(f) + local o = {} + function o.read() + return f() + end + return o +end + + +local function bitstream_from_bytestream(bys) + local buf_byte = 0 + local buf_nbit = 0 + local o = {} + + function o.nbits_left_in_byte() + return buf_nbit + end + + function o:read(nbits) + nbits = nbits or 1 + while buf_nbit < nbits do + local byte = bys:read() + if not byte then return end -- note: more calls also return nil + buf_byte = buf_byte + lshift(byte, buf_nbit) + buf_nbit = buf_nbit + 8 + end + local bits + if nbits == 0 then + bits = 0 + elseif nbits == 32 then + bits = buf_byte + buf_byte = 0 + else + bits = band(buf_byte, rshift(0xffffffff, 32 - nbits)) + buf_byte = rshift(buf_byte, nbits) + end + buf_nbit = buf_nbit - nbits + return bits + end + + + is_bitstream[o] = true + + return o +end + + +local function get_bitstream(o) + local bs + if is_bitstream[o] then + return o + elseif io.type(o) == 'file' then + bs = bitstream_from_bytestream(bytestream_from_file(o)) + elseif type(o) == 'string' then + bs = bitstream_from_bytestream(bytestream_from_string(o)) + elseif type(o) == 'function' then + bs = bitstream_from_bytestream(bytestream_from_function(o)) + else + runtime_error 'unrecognized type' + end + return bs +end + + +local function get_obytestream(o) + local bs + if io.type(o) == 'file' then + bs = function(sbyte) o:write(string_char(sbyte)) end + elseif type(o) == 'function' then + bs = o + else + runtime_error('unrecognized type: ' .. tostring(o)) + end + return bs +end + + +local function HuffmanTable(init, is_full) + local t = {} + if is_full then + for val,nbits in pairs(init) do + if nbits ~= 0 then + t[#t+1] = {val=val, nbits=nbits} + end + end + else + for i=1,#init-2,2 do + local firstval, nbits, nextval = init[i], init[i+1], init[i+2] + if nbits ~= 0 then + for val=firstval,nextval-1 do + t[#t+1] = {val=val, nbits=nbits} + end + end + end + end + table_sort(t, function(a,b) + return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits + end) + + -- assign codes + local code = 1 -- leading 1 marker + local nbits = 0 + for _,s in ipairs(t) do + if s.nbits ~= nbits then + code = code * pow2[s.nbits - nbits] + nbits = s.nbits + end + s.code = code + code = code + 1 + end + + local minbits = math.huge + local look = {} + for _,s in ipairs(t) do + minbits = math.min(minbits, s.nbits) + look[s.code] = s.val + end + + local msb = function(bits, nbits) + local res = 0 + for _=1,nbits do + res = lshift(res, 1) + band(bits, 1) + bits = rshift(bits, 1) + end + return res + end + + local tfirstcode = memoize( + function(bits) return pow2[minbits] + msb(bits, minbits) end) + + function t:read(bs) + local code = 1 -- leading 1 marker + local nbits = 0 + while 1 do + if nbits == 0 then -- small optimization (optional) + code = tfirstcode[noeof(bs:read(minbits))] + nbits = nbits + minbits + else + local b = noeof(bs:read()) + nbits = nbits + 1 + code = code * 2 + b -- MSB first + end + local val = look[code] + if val then + return val + end + end + end + + return t +end + + +local function parse_gzip_header(bs) + -- local FLG_FTEXT = 2^0 + local FLG_FHCRC = 2^1 + local FLG_FEXTRA = 2^2 + local FLG_FNAME = 2^3 + local FLG_FCOMMENT = 2^4 + + local id1 = bs:read(8) + local id2 = bs:read(8) + if id1 ~= 31 or id2 ~= 139 then + runtime_error 'not in gzip format' + end + bs:read(8) -- compression method + local flg = bs:read(8) -- FLaGs + local mtime = bs:read(32) -- Modification TIME + local xfl = bs:read(8) -- eXtra FLags + local os = bs:read(8) -- Operating System + + if not os then runtime_error 'invalid header' end + + if hasbit(flg, FLG_FEXTRA) then + local xlen = bs:read(16) + local extra = 0 + for i=1,xlen do + extra = bs:read(8) + end + if not extra then runtime_error 'invalid header' end + end + + local function parse_zstring(bs) + repeat + local by = bs:read(8) + if not by then runtime_error 'invalid header' end + until by == 0 + end + + if hasbit(flg, FLG_FNAME) then + parse_zstring(bs) + end + + if hasbit(flg, FLG_FCOMMENT) then + parse_zstring(bs) + end + + if hasbit(flg, FLG_FHCRC) then + local crc16 = bs:read(16) + if not crc16 then runtime_error 'invalid header' end + -- IMPROVE: check CRC. where is an example .gz file that + -- has this set? + end +end + +local function parse_zlib_header(bs) + local cm = bs:read(4) -- Compression Method + local cinfo = bs:read(4) -- Compression info + local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG) + local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary) + local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level) + local cmf = cinfo * 16 + cm -- CMF (Compresion Method and flags) + local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs + + if cm ~= 8 then -- not "deflate" + runtime_error("unrecognized zlib compression method: " + cm) + end + if cinfo > 7 then + runtime_error("invalid zlib window size: cinfo=" + cinfo) + end + local window_size = 2^(cinfo + 8) + + if (cmf*256 + flg) % 31 ~= 0 then + runtime_error("invalid zlib header (bad fcheck sum)") + end + + if fdict == 1 then + runtime_error("FIX:TODO - FDICT not currently implemented") + local dictid_ = bs:read(32) + end + + return window_size +end + +local function parse_huffmantables(bs) + local hlit = bs:read(5) -- # of literal/length codes - 257 + local hdist = bs:read(5) -- # of distance codes - 1 + local hclen = noeof(bs:read(4)) -- # of code length codes - 4 + + local ncodelen_codes = hclen + 4 + local codelen_init = {} + local codelen_vals = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} + for i=1,ncodelen_codes do + local nbits = bs:read(3) + local val = codelen_vals[i] + codelen_init[val] = nbits + end + local codelentable = HuffmanTable(codelen_init, true) + + local function decode(ncodes) + local init = {} + local nbits + local val = 0 + while val < ncodes do + local codelen = codelentable:read(bs) + --FIX:check nil? + local nrepeat + if codelen <= 15 then + nrepeat = 1 + nbits = codelen + elseif codelen == 16 then + nrepeat = 3 + noeof(bs:read(2)) + -- nbits unchanged + elseif codelen == 17 then + nrepeat = 3 + noeof(bs:read(3)) + nbits = 0 + elseif codelen == 18 then + nrepeat = 11 + noeof(bs:read(7)) + nbits = 0 + else + error 'ASSERT' + end + for i=1,nrepeat do + init[val] = nbits + val = val + 1 + end + end + local huffmantable = HuffmanTable(init, true) + return huffmantable + end + + local nlit_codes = hlit + 257 + local ndist_codes = hdist + 1 + + local littable = decode(nlit_codes) + local disttable = decode(ndist_codes) + + return littable, disttable +end + + +local tdecode_len_base +local tdecode_len_nextrabits +local tdecode_dist_base +local tdecode_dist_nextrabits +local function parse_compressed_item(bs, outstate, littable, disttable) + local val = littable:read(bs) + if val < 256 then -- literal + output(outstate, val) + elseif val == 256 then -- end of block + return true + else + if not tdecode_len_base then + local t = {[257]=3} + local skip = 1 + for i=258,285,4 do + for j=i,i+3 do t[j] = t[j-1] + skip end + if i ~= 258 then skip = skip * 2 end + end + t[285] = 258 + tdecode_len_base = t + end + if not tdecode_len_nextrabits then + local t = {} + + for i=257,285 do + local j = math_max(i - 261, 0) + t[i] = rshift(j, 2) + end + + t[285] = 0 + tdecode_len_nextrabits = t + end + local len_base = tdecode_len_base[val] + local nextrabits = tdecode_len_nextrabits[val] + local extrabits = bs:read(nextrabits) + local len = len_base + extrabits + + if not tdecode_dist_base then + local t = {[0]=1} + local skip = 1 + for i=1,29,2 do + for j=i,i+1 do t[j] = t[j-1] + skip end + if i ~= 1 then skip = skip * 2 end + end + tdecode_dist_base = t + end + if not tdecode_dist_nextrabits then + local t = {} + + for i=0,29 do + local j = math_max(i - 2, 0) + t[i] = rshift(j, 1) + end + + tdecode_dist_nextrabits = t + end + local dist_val = disttable:read(bs) + local dist_base = tdecode_dist_base[dist_val] + local dist_nextrabits = tdecode_dist_nextrabits[dist_val] + local dist_extrabits = bs:read(dist_nextrabits) + local dist = dist_base + dist_extrabits + + for i=1,len do + local pos = (outstate.window_pos - 1 - dist) % 32768 + 1 -- 32K + output(outstate, assert(outstate.window[pos], 'invalid distance')) + end + end + return false +end + + +local function parse_block(bs, outstate, throttle) + local bfinal = bs:read(1) + local btype = bs:read(2) + + local BTYPE_NO_COMPRESSION = 0 + local BTYPE_FIXED_HUFFMAN = 1 + local BTYPE_DYNAMIC_HUFFMAN = 2 + local BTYPE_RESERVED_ = 3 + + if btype == BTYPE_NO_COMPRESSION then + bs:read(bs:nbits_left_in_byte()) + local len = bs:read(16) + local nlen_ = noeof(bs:read(16)) + + for _=1,len do + local by = noeof(bs:read(8)) + output(outstate, by) + end + elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then + local littable, disttable + if btype == BTYPE_DYNAMIC_HUFFMAN then + littable, disttable = parse_huffmantables(bs) + else + littable = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil} + disttable = HuffmanTable {0,5, 32,nil} + end + + repeat + local is_done = parse_compressed_item( + bs, outstate, littable, disttable) + throttle() + until is_done + else + runtime_error 'unrecognized compression type' + end + + return bfinal ~= 0 +end + +function M.inflate(t) + local bs = get_bitstream(t.input) + local outbs = get_obytestream(t.output) + local outstate = make_outstate(outbs) + + repeat + local is_final = parse_block(bs, outstate, t.throttle) + until is_final +end +local inflate = M.inflate + +function M.gunzip(t) + local bs = get_bitstream(t.input) + local outbs = get_obytestream(t.output) + + parse_gzip_header(bs) + + inflate{input=bs, output=outbs, throttle=t.throttle or function() end} + + bs:read(bs:nbits_left_in_byte()) +end + +return M \ No newline at end of file diff --git a/compress/apis/lzw.lua b/compress/apis/lzw.lua new file mode 100644 index 0000000..3d4c4b5 --- /dev/null +++ b/compress/apis/lzw.lua @@ -0,0 +1,142 @@ +-- see: https://github.com/Rochet2/lualzw +-- MIT License - Copyright (c) 2016 Rochet2 + +local char = string.char +local type = type +local sub = string.sub +local tconcat = table.concat + +local SIGC = 'LZWC' + +local basedictcompress = {} +local basedictdecompress = {} +for i = 0, 255 do + local ic, iic = char(i), char(i, 0) + basedictcompress[ic] = iic + basedictdecompress[iic] = ic +end + +local function dictAddA(str, dict, a, b) + if a >= 256 then + a, b = 0, b+1 + if b >= 256 then + dict = {} + b = 1 + end + end + dict[str] = char(a,b) + a = a+1 + return dict, a, b +end + +local function compress(input) + if type(input) ~= "string" then + error ("string expected, got "..type(input)) + end + local len = #input + if len <= 1 then + return input + end + + local dict = {} + local a, b = 0, 1 + + local result = { SIGC } + local resultlen = 1 + local n = 2 + local word = "" + for i = 1, len do + local c = sub(input, i, i) + local wc = word..c + if not (basedictcompress[wc] or dict[wc]) then + local write = basedictcompress[word] or dict[word] + if not write then + error "algorithm error, could not fetch word" + end + result[n] = write + resultlen = resultlen + #write + n = n+1 + if len <= resultlen then + return input + end + dict, a, b = dictAddA(wc, dict, a, b) + word = c + else + word = wc + end + end + result[n] = basedictcompress[word] or dict[word] + resultlen = resultlen+#result[n] + if len <= resultlen then + return input + end + return tconcat(result) +end + +local function dictAddB(str, dict, a, b) + if a >= 256 then + a, b = 0, b+1 + if b >= 256 then + dict = {} + b = 1 + end + end + dict[char(a,b)] = str + a = a+1 + return dict, a, b +end + +local function decompress(input) + if type(input) ~= "string" then + error( "string expected, got "..type(input)) + end + + if #input <= 1 then + return input + end + + local control = sub(input, 1, 4) + if control ~= SIGC then + return input + end + input = sub(input, 5) + local len = #input + + if len < 2 then + error("invalid input - not a compressed string") + end + + local dict = {} + local a, b = 0, 1 + + local result = {} + local n = 1 + local last = sub(input, 1, 2) + result[n] = basedictdecompress[last] or dict[last] + n = n+1 + for i = 3, len, 2 do + local code = sub(input, i, i+1) + local lastStr = basedictdecompress[last] or dict[last] + if not lastStr then + error( "could not find last from dict. Invalid input?") + end + local toAdd = basedictdecompress[code] or dict[code] + if toAdd then + result[n] = toAdd + n = n+1 + dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b) + else + local tmp = lastStr..sub(lastStr, 1, 1) + result[n] = tmp + n = n+1 + dict, a, b = dictAddB(tmp, dict, a, b) + end + last = code + end + return tconcat(result) +end + +return { + compress = compress, + decompress = decompress, +} diff --git a/compress/apis/tar.lua b/compress/apis/tar.lua new file mode 100644 index 0000000..c64a9be --- /dev/null +++ b/compress/apis/tar.lua @@ -0,0 +1,211 @@ + +-- see: https://github.com/luarocks/luarocks/blob/master/src/luarocks/tools/tar.lua +-- A pure-Lua implementation of untar (unpacking .tar archives) + +local tar = { } + +local fs = _G.fs + +local blocksize = 512 + +local function get_typeflag(flag) + if flag == "0" or flag == "\0" then return "file" + elseif flag == "1" then return "link" + elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU + elseif flag == "3" then return "character" + elseif flag == "4" then return "block" + elseif flag == "5" then return "directory" + elseif flag == "6" then return "fifo" + elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU + elseif flag == "x" then return "next file" + elseif flag == "g" then return "global extended header" + elseif flag == "L" then return "long name" + elseif flag == "K" then return "long link name" + end + return "unknown" +end + +local function octal_to_number(octal) + local exp = 0 + local number = 0 + octal = octal:gsub("%s", "") + for i = #octal,1,-1 do + local digit = tonumber(octal:sub(i,i)) + if not digit then + break + end + number = number + (digit * 8^exp) + exp = exp + 1 + end + return number +end + +local function checksum_header(block) + local sum = 256 + for i = 1,148 do + local b = block:byte(i) or 0 + sum = sum + b + end + for i = 157,500 do + local b = block:byte(i) or 0 + sum = sum + b + end + return sum +end + +local function nullterm(s) +_G._zz = s + return s:match("^[^%z]*") +end + +local function read_header_block(block) + local header = {} + header.name = nullterm(block:sub(1,100)) + header.mode = nullterm(block:sub(101,108)):gsub(" ", "") + header.uid = octal_to_number(nullterm(block:sub(109,116))) + header.gid = octal_to_number(nullterm(block:sub(117,124))) + header.size = octal_to_number(nullterm(block:sub(125,136))) + header.mtime = octal_to_number(nullterm(block:sub(137,148))) + header.chksum = octal_to_number(nullterm(block:sub(149,156))) + header.typeflag = get_typeflag(block:sub(157,157)) + header.linkname = nullterm(block:sub(158,257)) + header.magic = block:sub(258,263) + header.version = block:sub(264,265) + header.uname = nullterm(block:sub(266,297)) + header.gname = nullterm(block:sub(298,329)) + header.devmajor = octal_to_number(nullterm(block:sub(330,337))) + header.devminor = octal_to_number(nullterm(block:sub(338,345))) + header.prefix = block:sub(346,500) + if not checksum_header(block) == header.chksum then + return false, "Failed header checksum" + end + return header +end + +function tar.untar(filename, destdir, verbose) + assert(type(filename) == "string") + assert(type(destdir) == "string") + + local tar_handle = io.open(filename, "rb") + if not tar_handle then return nil, "Error opening file "..filename end + + local long_name, long_link_name + local ok, err + + local make_dir = function(a) + if not fs.exists(a) then + fs.makeDir(a) + end + return true + end + + while true do + local block + repeat + block = tar_handle:read(blocksize) + until (not block) or checksum_header(block) > 256 + if not block then break end + if #block < blocksize then + ok, err = nil, "Invalid block size -- corrupted file?" + break + end + local header + header, err = read_header_block(block) + if not header then + ok = false + break + end + + local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size) + + if header.typeflag == "long name" then + long_name = nullterm(file_data) + elseif header.typeflag == "long link name" then + long_link_name = nullterm(file_data) + else + if long_name then + header.name = long_name + long_name = nil + end + if long_link_name then + header.name = long_link_name + long_link_name = nil + end + end + local pathname = fs.combine(destdir, header.name) + + if header.typeflag == "directory" then + ok, err = make_dir(pathname) + if not ok then + break + end + elseif header.typeflag == "file" then + local dirname = fs.getDir(pathname) + if dirname ~= "" then + ok, err = make_dir(dirname) + if not ok then + break + end + end + local file_handle + if verbose then + print(pathname) + end + file_handle, err = io.open(pathname, "wb") + if not file_handle then + ok = nil + break + end + file_handle:write(file_data) + file_handle:close() + end + end + tar_handle:close() + return ok, err +end + +local function create_header_block(filename, abspath) + local block = ('\0'):rep(512) + + local function number_to_octal(n) + return ('%o'):format(n) + end + + local function ins(pos, istr) + block = block:sub(1, pos - 1) .. istr .. block:sub(pos + #istr) + end + + ins(1, filename) -- header + ins(125, number_to_octal(fs.getSize(abspath))) + ins(157, '0') -- typeflag + + ins(149, number_to_octal(checksum_header(block))) + + return block +end + +-- the bare minimum for this program to untar +function tar.tar(filename, root, files) + assert(type(filename) == "string") + assert(type(root) == "string") + assert(type(files) == "table") + + local tar_handle = io.open(filename, "wb") + if not tar_handle then return nil, "Error opening file "..filename end + + for _, file in pairs(files) do + local abs = fs.combine(root, file) + local block = create_header_block(file, abs) + tar_handle:write(block) + local f = require('opus.util').readFile(abs, 'rb') + tar_handle:write(f) + local padding = #f % 512 + if padding > 0 then + tar_handle:write(('\0'):rep(512 - padding)) + end + end + tar_handle:close() + return true +end + +return tar \ No newline at end of file diff --git a/compress/untar.lua b/compress/untar.lua new file mode 100644 index 0000000..080c9d4 --- /dev/null +++ b/compress/untar.lua @@ -0,0 +1,46 @@ +-- see: https://github.com/luarocks/luarocks/blob/master/src/luarocks/tools/tar.lua + +-- TODO: support normal tar syntax -- tar xvf + +local DEFLATE = require('compress.deflatelua') +local tar = require('compress.tar') + +local fs = _G.fs +local io = _G.io +local shell = _ENV.shell + +local args = { ... } + +if not args[2] then + error('Syntax: tar FILE DESTDIR') +end + +local inFile = shell.resolve(args[1]) +local outDir = shell.resolve(args[2]) + +if inFile:match('(.+)%.[gG][zZ]$') then + local TMP_FILE = '.out.tar' + + local fh = io.open(inFile, 'rb') or error('Error opening ' .. inFile) + + fs.mount(TMP_FILE, 'ramfs', 'file') + local ofh = io.open(TMP_FILE, 'wb') + + DEFLATE.gunzip {input=fh, output=ofh, disable_crc=true} + + fh:close() + ofh:close() + + local s, m = tar.untar(TMP_FILE, outDir, true) + + fs.delete(TMP_FILE) + + if not s then + error(m) + end +else + local s, m = tar.untar(inFile, outDir) + if not s then + error(m) + end +end diff --git a/debugger/apis/init.lua b/debugger/apis/init.lua index 44e4b16..a7b97d3 100644 --- a/debugger/apis/init.lua +++ b/debugger/apis/init.lua @@ -9,10 +9,33 @@ local dbg = { breakpoints = nil, } +local romFiles = { + load = function(self) + local function recurse(dir) + local files = fs.list(dir) + for _,f in ipairs(files) do + local fullName = fs.combine(dir, f) + if fs.isDir(fullName) then + recurse(fullName) + else + self.files[f] = fullName + end + end + end + recurse('rom/apis') + end, + get = function(self, file) + return self.files[file] + end, + files = { }, +} +romFiles:load() + local function breakpointHook(info) if dbg.breakpoints then + local src = romFiles:get(info.short_src) or info.short_src for _,v in pairs(dbg.breakpoints) do - if v.line == info.currentline and v.file == info.short_src then + if v.line == info.currentline and v.file == src then return not v.disabled end end diff --git a/debugger/debug.lua b/debugger/debug.lua index b26b675..e2cfbe3 100644 --- a/debugger/debug.lua +++ b/debugger/debug.lua @@ -376,7 +376,7 @@ local page = UI.Page { local line = self.source:getSelected().line multishell.openTab(_ENV, { path = 'sys/apps/shell.lua', - args = { ('edit --line=%d %s'):format(line , file) }, + args = { ('edit --line=%d %s'):format(line , '/' .. file) }, focused = true, }) end diff --git a/tests/test-ramfs.lua b/tests/test-ramfs.lua new file mode 100644 index 0000000..caa57bc --- /dev/null +++ b/tests/test-ramfs.lua @@ -0,0 +1,30 @@ +local ramfs = require('opus.fs.ramfs') + +local fs = _G.fs + +local str = 'hello' + +local node = fs.mount('test.bin', 'ramfs', 'file') +local file = ramfs.open(node, 'test.bin', 'wb') +for i = 1, 5 do + file.write(str:sub(i, i)) +end +file.close() + +node = fs.getNode('test.bin') +file = ramfs.open(node, 'test.bin', 'r') +local s = file.read(5) +file.close() + +assert(s == str) + +file = ramfs.open(node, 'test.bin', 'w') +file.write(str) +file.close() + +node = fs.getNode('test.bin') +file = ramfs.open(node, 'test.bin', 'rb') +s = file.read(5) +file.close() + +assert(s == str) diff --git a/tests/test-tar.lua b/tests/test-tar.lua new file mode 100644 index 0000000..272b3ed --- /dev/null +++ b/tests/test-tar.lua @@ -0,0 +1,20 @@ +local tar = require('compress.tar') +local Util = require('opus.util') + +local fs = _G.fs + +local files = { + 'test-ramfs.lua', + 'test-tar.lua', +} + +fs.mount('packages/tests/test.tar', 'ramfs', 'file') +tar.tar('packages/tests/test.tar', 'packages/tests', files) + +fs.mount('packages/tests/untar', 'ramfs', 'directory') +tar.untar('packages/tests/test.tar', 'packages/tests/untar') + +local s1 = Util.readFile('packages/tests/test-ramfs.lua', 'r') +local s2 = Util.readFile('packages/tests/untar/test-ramfs.lua', 'r') + +assert(s1 == s2)