diff options
author | William Wilgus <wilgus.william@gmail.com> | 2024-05-04 08:41:16 -0400 |
---|---|---|
committer | William Wilgus <wilgus.william@gmail.com> | 2024-05-05 14:01:43 -0400 |
commit | d7c541742f5e6ec07dbcc8e1346efde9d807437e (patch) | |
tree | 2c3325b652999bd9b2443eb96d8d32035d3c4c18 | |
parent | efcea6628024c5f6796c3850c3779801db4b6874 (diff) | |
download | rockbox-d7c541742f.tar.gz rockbox-d7c541742f.zip |
Allow first level folders in plugin menu
add sorting directories as files
move picross files to a hidden folder
use directory for lua_scripts, sgt_puzzles
make plugin browser able to handle 1st level directories
Change-Id: I30852d71dc992c378d5790756e94f06f5a2e9bef
-rw-r--r-- | apps/filetree.c | 68 | ||||
-rw-r--r-- | apps/plugins/CATEGORIES | 1 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/lua/include_lua/dbgettags.lua | 119 | ||||
-rwxr-xr-x | apps/plugins/lua/include_lua/filebrowse.lua | 241 | ||||
-rwxr-xr-x | apps/plugins/lua/include_lua/fileviewers.lua | 468 | ||||
-rw-r--r-- | apps/plugins/lua/lua.make | 12 | ||||
-rw-r--r-- | apps/plugins/lua_scripts/lua_scripts.lua | 172 | ||||
-rw-r--r-- | apps/plugins/lua_scripts/lua_scripts.make | 4 | ||||
-rw-r--r-- | apps/plugins/picross.lua | 8 | ||||
-rw-r--r-- | apps/plugins/picross/picross.make | 4 | ||||
-rw-r--r-- | apps/settings.h | 2 | ||||
-rw-r--r-- | apps/tree.c | 9 | ||||
-rwxr-xr-x | tools/buildzip.pl | 28 |
14 files changed, 1079 insertions, 58 deletions
diff --git a/apps/filetree.c b/apps/filetree.c index b5f5dece5a..594a0bd6f1 100644 --- a/apps/filetree.c +++ b/apps/filetree.c @@ -224,7 +224,11 @@ static int compare(const void* p1, const void* p2) struct entry* e2 = (struct entry*)p2; int criteria; - if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY) + if (cmp_data.sort_dir == SORT_AS_FILE) + { /* treat as two files */ + criteria = global_settings.sort_file; + } + else if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY) { /* two directories */ criteria = cmp_data.sort_dir; @@ -326,18 +330,17 @@ int ft_load(struct tree_context* c, const char* tempdir) info = dir_get_info(dir, entry); len = strlen((char *)entry->d_name); - /* skip directories . and .. */ - if ((info.attribute & ATTR_DIRECTORY) && - (((len == 1) && (!strncmp((char *)entry->d_name, ".", 1))) || - ((len == 2) && (!strncmp((char *)entry->d_name, "..", 2))))) { - continue; - } - /* Skip FAT volume ID */ if (info.attribute & ATTR_VOLUME_ID) { continue; } + dptr->attr = info.attribute; + int dir_attr = (dptr->attr & ATTR_DIRECTORY); + /* skip directories . and .. */ + if (dir_attr && is_dotdir_name(entry->d_name)) + continue; + /* filter out dotfiles and hidden files */ if (*c->dirfilter != SHOW_ALL && ((entry->d_name[0]=='.') || @@ -345,48 +348,45 @@ int ft_load(struct tree_context* c, const char* tempdir) continue; } - dptr->attr = info.attribute; - int dir_attr = (dptr->attr & ATTR_DIRECTORY); - /* check for known file types */ if ( !(dir_attr) ) dptr->attr |= filetype_get_attr((char *)entry->d_name); int file_attr = (dptr->attr & FILE_ATTR_MASK); +#define CHK_FT(show,attr) (*c->dirfilter == (show) && file_attr != (attr)) /* filter out non-visible files */ - if ((!(dir_attr) && ((*c->dirfilter == SHOW_PLAYLIST && - file_attr != FILE_ATTR_M3U) || - ((*c->dirfilter == SHOW_MUSIC && file_attr != FILE_ATTR_AUDIO) && - file_attr != FILE_ATTR_M3U) || + if ((!(dir_attr) && (CHK_FT(SHOW_PLAYLIST, FILE_ATTR_M3U) || + (CHK_FT(SHOW_MUSIC, FILE_ATTR_AUDIO) && file_attr != FILE_ATTR_M3U) || (*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)))) || - (*c->dirfilter == SHOW_WPS && file_attr != FILE_ATTR_WPS) || - (*c->dirfilter == SHOW_FONT && file_attr != FILE_ATTR_FONT) || - (*c->dirfilter == SHOW_SBS && file_attr != FILE_ATTR_SBS) || + CHK_FT(SHOW_WPS, FILE_ATTR_WPS) || + CHK_FT(SHOW_FONT, FILE_ATTR_FONT) || + CHK_FT(SHOW_SBS, FILE_ATTR_SBS) || #if CONFIG_TUNER - (*c->dirfilter == SHOW_FMS && file_attr != FILE_ATTR_FMS) || + CHK_FT(SHOW_FMS, FILE_ATTR_FMS) || + CHK_FT(SHOW_FMR, FILE_ATTR_FMR) || #endif #ifdef HAVE_REMOTE_LCD - (*c->dirfilter == SHOW_RWPS && file_attr != FILE_ATTR_RWPS) || - (*c->dirfilter == SHOW_RSBS && file_attr != FILE_ATTR_RSBS) || + CHK_FT(SHOW_RWPS, FILE_ATTR_RWPS) || + CHK_FT(SHOW_RSBS, FILE_ATTR_RSBS) || #if CONFIG_TUNER - (*c->dirfilter == SHOW_RFMS && file_attr != FILE_ATTR_RFMS) || -#endif + CHK_FT(SHOW_RFMS, FILE_ATTR_RFMS) || #endif -#if CONFIG_TUNER - (*c->dirfilter == SHOW_FMR && file_attr != FILE_ATTR_FMR) || #endif - (*c->dirfilter == SHOW_M3U && file_attr != FILE_ATTR_M3U) || - (*c->dirfilter == SHOW_CFG && file_attr != FILE_ATTR_CFG) || - (*c->dirfilter == SHOW_LNG && file_attr != FILE_ATTR_LNG) || - (*c->dirfilter == SHOW_MOD && file_attr != FILE_ATTR_MOD) || - (*c->dirfilter == SHOW_PLUGINS && file_attr != FILE_ATTR_ROCK && - file_attr != FILE_ATTR_LUA && - file_attr != FILE_ATTR_OPX) || + CHK_FT(SHOW_M3U, FILE_ATTR_M3U) || + CHK_FT(SHOW_CFG, FILE_ATTR_CFG) || + CHK_FT(SHOW_LNG, FILE_ATTR_LNG) || + CHK_FT(SHOW_MOD, FILE_ATTR_MOD) || + /* show first level directories */ + ((!(dir_attr) || c->dirlevel > 0) && + CHK_FT(SHOW_PLUGINS, FILE_ATTR_ROCK) && + file_attr != FILE_ATTR_LUA && + file_attr != FILE_ATTR_OPX) || (callback_show_item && !callback_show_item(entry->d_name, dptr->attr, c))) { continue; } +#undef CHK_FT if (len > c->cache.name_buffer_size - name_buffer_used - 1) { /* Tell the world that we ran out of buffer space */ @@ -408,7 +408,9 @@ int ft_load(struct tree_context* c, const char* tempdir) c->dirlength = files_in_dir; closedir(dir); - cmp_data.sort_dir = c->sort_dir; + /* allow directories to be sorted into file list */ + cmp_data.sort_dir = (*c->dirfilter == SHOW_PLUGINS) ? SORT_AS_FILE : c->sort_dir; + if (global_settings.sort_case) { if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER) diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index db0eb70fc4..c91d02ade8 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES @@ -57,7 +57,6 @@ lastfm_scrobbler_viewer,viewers logo,demos lrcplayer,apps lua,viewers -lua_scripts,demos fractals,demos main_menu_config,apps matrix,demos diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index ff08074f82..8028758ef0 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -117,7 +117,6 @@ metronome.c #if PLUGIN_BUFFER_SIZE >= 0x80000 boomshine.lua picross.lua -lua_scripts.lua #ifdef HAVE_LCD_COLOR pixel-painter.lua #endif /* HAVE_LCD_COLOR */ diff --git a/apps/plugins/lua/include_lua/dbgettags.lua b/apps/plugins/lua/include_lua/dbgettags.lua new file mode 100644 index 0000000000..8e9f26393d --- /dev/null +++ b/apps/plugins/lua/include_lua/dbgettags.lua @@ -0,0 +1,119 @@ +-- dbgettags.lua Bilgus 2017 +--[[ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2017 William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +]] + +require("actions") +local CANCEL_BUTTON = rb.actions.PLA_CANCEL + +local sINVALIDDATABASE = "Invalid Database" +local sERROROPENING = "Error opening" + +-- tag cache header +sTCVERSION = string.char(0x10) +sTCHEADER = string.reverse("TCH" .. sTCVERSION) +DATASZ = 4 -- int32_t +TCHSIZE = 3 * DATASZ -- 3 x int32_t + +-- Converts array of bytes to proper endian +function bytesLE_n(str) + str = str or "" + local tbyte={str:byte(1, -1)} + local bpos = 1 + local num = 0 + for k = 1,#tbyte do -- (k = #t, 1, -1 for BE) + num = num + tbyte[k] * bpos + bpos = bpos * 256 + end + return num +end + +-- uses database files to retrieve database tags +-- adds all unique tags into a lua table +-- ftable is optional +function get_tags(filename, hstr, ftable) + + if not filename then return end + if not ftable then ftable = {} end + hstr = hstr or filename + + local file = io.open('/' .. filename or "", "r") --read + if not file then rb.splash(100, sERROROPENING .. " " .. filename) return end + + local fsz = file:seek("end") + + local posln = 0 + local tag_len = TCHSIZE + local idx + + local function readchrs(count) + if posln >= fsz then return nil end + file:seek("set", posln) + posln = posln + count + return file:read(count) + end + + -- check the header and get size + #entries + local tagcache_header = readchrs(DATASZ) or "" + local tagcache_sz = readchrs(DATASZ) or "" + local tagcache_entries = readchrs(DATASZ) or "" + + if tagcache_header ~= sTCHEADER or + bytesLE_n(tagcache_sz) ~= (fsz - TCHSIZE) then + rb.splash(100, sINVALIDDATABASE .. " " .. filename) + return + end + + -- local tag_entries = bytesLE_n(tagcache_entries) + + for k, v in pairs(ftable) do ftable[k] = nil end -- clear table + ftable[1] = hstr + + local tline = #ftable + 1 + ftable[tline] = "" + + local str = "" + + while true do + tag_len = bytesLE_n(readchrs(DATASZ)) + readchrs(DATASZ) -- idx = bytesLE_n(readchrs(DATASZ)) + str = readchrs(tag_len) or "" + str = string.match(str, "(%Z+)%z") -- \0 terminated string + + if str then + if ftable[tline - 1] ~= str then -- Remove dupes + ftable[tline] = str + tline = tline + 1 + end + elseif posln >= fsz then + break + end + + if rb.get_plugin_action(0) == CANCEL_BUTTON then + break + end + end + + file:close() + + return ftable +end -- get_tags diff --git a/apps/plugins/lua/include_lua/filebrowse.lua b/apps/plugins/lua/include_lua/filebrowse.lua new file mode 100755 index 0000000000..0640ec3764 --- /dev/null +++ b/apps/plugins/lua/include_lua/filebrowse.lua @@ -0,0 +1,241 @@ +--[[ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2017 William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +]] +if ... == nil then rb.splash(rb.HZ * 3, "use 'require'") end +require("printtable") +local _lcd = require("lcd") +local _timer = require("timer") + +-------------------------------------------------------------------------------- +--[[ returns a sorted tables of directories and (another) of files +-- path is the starting path; norecurse == true.. only that path will be searched +-- findfile & finddir are definable search functions +-- if not defined all files/dirs are returned if false is passed.. none +-- or you can provide your own function see below.. +-- f_t and d_t allow you to pass your own tables for re-use but isn't necessary +]] +local function get_files(path, norecurse, finddir, findfile, sort_by, f_t, d_t) + local quit = false + local sort_by_function -- forward declaration + local filepath_function -- forward declaration + local files = f_t or {} + local dirs = d_t or {} + + local function f_filedir(name) + --default find function + -- example: return name:find(".mp3", 1, true) ~= nil + if name:len() <= 2 and (name == "." or name == "..") then + return false + end + return true + end + local function d_filedir(name) + --default discard function + return false + end + + if finddir == nil then + finddir = f_filedir + elseif type(finddir) ~= "function" then + finddir = d_filedir + end + + if findfile == nil then + findfile = f_filedir + elseif type(findfile) ~= "function" then + findfile = d_filedir + end + + local function _get_files(path, cancelbtn) + local sep = "" + local filepath + local finfo_t + if string.sub(path, - 1) ~= "/" then sep = "/" end + for fname, isdir, finfo_t in luadir.dir(path, true) do + if isdir and finddir(fname) then + table.insert(dirs, path .. sep ..fname) + elseif not isdir and findfile(fname) then + filepath = filepath_function(path, sep, fname, finfo_t.attribute, finfo_t.size, finfo_t.time) + table.insert(files, filepath) + end + + if rb.get_plugin_action(0) == cancelbtn then + return true + end + end + end + + + + local function cmp_alphanum (op1, op2) + local type1= type(op1) + local type2 = type(op2) + + if type1 ~= type2 then + return type1 < type2 + else + if type1 == "string" then + op1 = op1:upper() + op2 = op2:upper() + return sort_by_function(op1, op2) + end + return op1 < op2 + end + end + + _lcd:splashf(1, "Searching for Files") + + if sort_by == "name" then + sort_by_function = function(s1, s2) return s1 < s2 end + filepath_function = function(path, sep, fname, fattrib, fsize, ftime) + return string.format("%s%s%s;", path, sep, fname) + end + elseif sort_by == "size" then + filepath_function = function(path, sep, fname, fattrib, fsize, ftime) + return string.format("%s%s%s; At:%d, Sz:%d, Tm:%d", path, sep, fname, fattrib, fsize, ftime) + end + sort_by_function = function(s1, s2) + local v1, v2 + v1 = string.match(s1, "SZ:(%d+)") + v2 = string.match(s2, "SZ:(%d+)") + if v1 or v2 then + return tonumber(v1 or 0) < tonumber(v2 or 0) + end + return s1 < s2 + end + elseif sort_by == "date" then + filepath_function = function(path, sep, fname, fattrib, fsize, ftime) + return string.format("%s%s%s; At:%d, Sz:%d, Tm:%d", path, sep, fname, fattrib, fsize, ftime) + end + sort_by_function = function(s1, s2) + local v1, v2 + v1 = string.match(s1, "TM:(%d+)") + v2 = string.match(s2, "TM:(%d+)") + if v1 or v2 then + return tonumber(v1 or 0) < tonumber(v2 or 0) + end + return s1 < s2 + end + end + + table.insert(dirs, path) -- root + + for key,value in pairs(dirs) do + --luadir.dir may error out so we need to do the call protected + -- _get_files(value, CANCEL_BUTTON) + _, quit = pcall(_get_files, value, CANCEL_BUTTON) + + if quit == true or norecurse then + break; + end + end + + table.sort(files, cmp_alphanum) + table.sort(dirs, cmp_alphanum) + + return dirs, files +end -- get_files +-------------------------------------------------------------------------------- + +-- uses print_table and get_files to display simple file browser +-- sort_by "date" "name" "size" +-- descending true/false +function file_choose(dir, title, sort_by, descending) + local dstr, hstr = "" + if not title then + dstr = "%d items found in %0d.%02d seconds" + else + hstr = title + end + + if not sort_by then sort_by = "name" end + sort_by = sort_by:lower() + + -- returns whole seconds and remainder + local function tick2seconds(ticks) + local secs = (ticks / rb.HZ) + local csecs = (ticks - (secs * rb.HZ)) + return secs, csecs + end + + local norecurse = true + local f_finddir = nil -- function to match directories; nil all, false none + local f_findfile = nil -- function to match files; nil all, false none + + local p_settings = {wrap = true, hasheader = true} + + local timer + local files = {} + local dirs = {} + local item = 1 + _lcd:clear() + + while item > 0 do + if not title then + timer = _timer() + end + + dirs, files = get_files(dir, norecurse, f_finddir, f_findfile, sort_by, dirs, files) + + local parentdir = dirs[1] + for i = 1, #dirs do + dirs[i] = "\t" .. dirs[i] + end + + if not descending then + for i = 1, #files do + -- only store file name .. strip attributes from end + table.insert(dirs, "\t" .. string.match(files[i], "[^;]+") or "?") + end + else + for i = #files, 1, -1 do + -- only store file name .. strip attributes from end + table.insert(dirs, "\t" .. string.match(files[i], "[^;]+") or "?") + end + end + for i=1, #files do files[i] = nil end -- empty table for reuse + + if not title then + hstr = string.format(dstr, #dirs - 1, tick2seconds(timer:stop())) + end + + table.insert(dirs, 1, hstr) + + item = print_table(dirs, #dirs, p_settings) + + -- If item was selected follow directory or return filename + if item > 0 then + dir = string.gsub(dirs[item], "%c+","") + if not rb.dir_exists("/" .. dir) then + return dir + end + end + + if dir == parentdir then + dir = dir:sub(1, dir:match(".*()/") - 1) + if dir == "" then dir = "/" end + end + for i=1, #dirs do dirs[i] = nil end -- empty table for reuse + + end +end -- file_choose +-------------------------------------------------------------------------------- diff --git a/apps/plugins/lua/include_lua/fileviewers.lua b/apps/plugins/lua/include_lua/fileviewers.lua new file mode 100755 index 0000000000..c686f3eeda --- /dev/null +++ b/apps/plugins/lua/include_lua/fileviewers.lua @@ -0,0 +1,468 @@ +--[[ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2017 William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +]] +if ... == nil then rb.splash(rb.HZ * 3, "use 'require'") end +require("printtable") +local _clr = require("color") +local _lcd = require("lcd") +local _print = require("print") +local _timer = require("timer") + +require("actions") +local CANCEL_BUTTON = rb.actions.PLA_CANCEL +-------------------------------------------------------------------------------- +-- builds an index of byte position of every line at each bufsz increment +-- in filename; bufsz == 1 would be every line; saves to filename.ext.idx_ext +-- lnbyte should be nil for text files and number of bytes per line for binary +local function build_file_index(filename, idx_ext, bufsz, lnbyte) + + if not filename then return end + local file = io.open('/' .. filename, "r") --read + if not file then _lcd:splashf(100, "Can't open %s", filename) return end + local fsz = file:seek("end") + local fsz_kb = fsz / 1024 + local count + local ltable = {0} --first index is the beginning of the file + local timer = _timer() + local fread + _lcd:splashf(100, "Indexing file %d Kb", (fsz / 1024)) + + if lnbyte then + fread = function(f) return f:read(lnbyte) end + else + lnbyte = -1 + fread = function(f) return f:read("*l") end + end + + file:seek("set", 0) + for i = 1, fsz do + if i % bufsz == 0 then + local loc = file:seek() + ltable[#ltable + 1] = loc + _lcd:splashf(1, "Parsing %d of %d Kb", loc / 1024, fsz_kb) + end + if rb.get_plugin_action(0) == CANCEL_BUTTON then + return + end + if not fread(file) then + count = i + break + end + end + + local fileidx = io.open('/' .. filename .. idx_ext, "w+") -- write/erase + if fileidx then + fileidx:write(fsz .. "\n") + fileidx:write(count .. "\n") + fileidx:write(bufsz .. "\n") + fileidx:write(lnbyte .. "\n") + fileidx:write(table.concat(ltable, "\n")) + fileidx:close() + _lcd:splashf(100, "Finished in %d seconds", timer.stop() / rb.HZ) + collectgarbage("collect") + else + error("unable to save index file") + end +end -- build_file_index +-------------------------------------------------------------------------------- + +--- returns size of original file, total lines buffersize, and table filled +-- with line offsets in index file -> filename +local function load_index_file(filename) + local filesz, count, bufsz, lnbyte + local ltable + local fileidx = io.open('/' .. filename, "r") --read + if fileidx then + local idx = -3 + ltable = {} + fileidx:seek("set", 0) + for line in fileidx:lines() do + if idx == -3 then + filesz = tonumber(line) + elseif idx == -2 then + count = tonumber(line) + elseif idx == -1 then + bufsz = tonumber(line) + elseif idx == 0 then + lnbyte = tonumber(line) + else + ltable[idx] = tonumber(line) + end + idx = idx + 1 + end + fileidx:close() + end + return lnbyte, filesz, count, bufsz, ltable +end -- load_index_file +-------------------------------------------------------------------------------- + +-- creates a fixed index with fixed line lengths, perfect for viewing hex files +-- not so great for reading text files but works as a fallback +local function load_fixed_index(bytesperline, filesz, bufsz) + local lnbyte = bytesperline + local count = (filesz + lnbyte - 1) / lnbyte + 1 + local idx_t = {} -- build index + for i = 0, filesz, bufsz do + idx_t[#idx_t + 1] = lnbyte * i + end + return lnbyte, filesz, count, bufsz, idx_t +end -- load_fixed_index +-------------------------------------------------------------------------------- + +-- uses print_table to display a whole file +function print_file(filename, maxlinelen, settings) + + if not filename then return end + local file = io.open('/' .. filename or "", "r") --read + if not file then _lcd:splashf(100, "Can't open %s", filename) return end + maxlinelen = 33 + local hstr = filename + local ftable = {} + table.insert(ftable, 1, hstr) + + local tline = #ftable + 1 + local remln = maxlinelen + local posln = 1 + + for line in file:lines() do + if line then + if maxlinelen then + if line == "" then + ftable[tline] = ftable[tline] or "" + tline = tline + 1 + remln = maxlinelen + else + line = line:match("%w.+") or "" + end + local linelen = line:len() + while linelen > 0 do + + local fsp = line:find("%s", posln + remln - 5) or 0x0 + fsp = fsp - (posln + remln) + if fsp >= 0 then + local fspr = fsp + fsp = line:find("%s", posln + remln) or linelen + fsp = fsp - (posln + remln) + if math.abs(fspr) < fsp then fsp = fspr end + end + if fsp > 5 or fsp < -5 then fsp = 0 end + + local str = line:sub(posln, posln + remln + fsp) + local slen = str:len() + ftable[tline] = ftable[tline] or "" + ftable[tline] = ftable[tline] .. str + linelen = linelen - slen + if linelen > 0 then + tline = tline + 1 + posln = posln + slen + remln = maxlinelen + --loop continues + else + ftable[tline] = ftable[tline] .. " " + remln = maxlinelen - slen + posln = 1 + --loop ends + end + + end + else + ftable[#ftable + 1] = line + end + + + end + end + + file:close() + + _lcd:clear() + _print.clear() + + if not settings then + settings = {} + settings.justify = "center" + settings.wrap = true + settings.msel = true + end + settings.hasheader = true + settings.co_routine = nil + settings.ovfl = "manual" + + local sel = + print_table(ftable, #ftable, settings) + + _lcd:splashf(rb.HZ * 2, "%d items {%s}", #sel, table.concat(sel, ", ")) + ftable = nil +end -- print_file +-------------------------------------------------------------------------------- + +-- uses print_table to display a portion of a file +function print_file_increment(filename, settings) + + if not filename then return end + local file = io.open('/' .. filename, "r") --read + if not file then _lcd:splashf(100, "Can't open %s", filename) return end + local fsz = file:seek("end") + local bsz = 1023 + --if small file do it the easier way and load whole file to table + if fsz < 60 * 1024 then + file:close() + print_file(filename, settings) + return + end + + local ext = ".idx" + local lnbyte, filesz, count, bufsz, idx_t = load_index_file(filename .. ext) + + if not idx_t or fsz ~= filesz then -- build file index + build_file_index(filename, ext, bsz) + lnbyte, filesz, count, bufsz, idx_t = load_index_file(filename .. ext) + end + + -- if invalid or user canceled creation fallback to a fixed index + if not idx_t or fsz ~= filesz or count <= 0 then + _lcd:splashf(rb.HZ * 5, "Unable to read file index %s", filename .. ext) + lnbyte, filesz, count, bufsz, idx_t = load_fixed_index(32, fsz, bsz) + end + + if not idx_t or fsz ~= filesz or count <= 0 then + _lcd:splashf(rb.HZ * 5, "Unable to load file %s", filename) + return + end + + local hstr = filename + local file_t = setmetatable({},{__mode = "kv"}) --weak keys and values + -- this allows them to be garbage collected as space is needed + -- rebuilds when needed + local ovf = 0 + local lpos = 1 + local timer = _timer() + file:seek("set", 0) + + function print_co() + while true do + collectgarbage("step") + file_t[1] = hstr --position 1 is ALWAYS header/title + + for i = 1, bufsz + ovf do + file_t[lpos + i] = file:read ("*l") + end + ovf = 0 + lpos = lpos + bufsz + + local bpos = coroutine.yield() + + if bpos <= lpos then -- roll over or scroll up + bpos = (bpos - bufsz) + bpos % bufsz + timer:check(true) + end + + lpos = bpos - bpos % bufsz + + if lpos < 1 then + lpos = 1 + elseif lpos > count - bufsz then -- partial fill + ovf = count - bufsz - lpos + end + --get position in file of the nearest indexed line + file:seek("set", idx_t[bpos / bufsz + 1]) + + -- on really large files if it has been more than 10 minutes + -- since the user scrolled up the screen wipe out the prior + -- items to free memory + if lpos % 5000 == 0 and timer:check() > rb.HZ * 600 then + for i = 1, lpos - 100 do + file_t[i] = nil + end + end + + end + end + + co = coroutine.create(print_co) + _lcd:clear() + _print.clear() + + if not settings then + settings = {} + settings.justify = "center" + settings.wrap = true + end + settings.hasheader = true + settings.co_routine = co + settings.msel = false + settings.ovfl = "manual" + + table.insert(file_t, 1, hstr) --position 1 is header/title + local sel = + print_table(file_t, count, settings) + file:close() + idx_t = nil + file_t = nil + return sel +end --print_file_increment +-------------------------------------------------------------------------------- +function print_file_hex(filename, bytesperline, settings) + + if not filename then return end + local file = io.open('/' .. filename, "r") --read + if not file then _lcd:splashf(100, "Can't open %s", filename) return end + local hstr = filename + local bpl = bytesperline + local fsz = file:seek("end") +--[[ + local filesz = file:seek("end") + local bufsz = 1023 + local lnbyte = bytesperline + local count = (filesz + lnbyte - 1) / lnbyte + 1 + + local idx_t = {} -- build index + for i = 0, filesz, bufsz do + idx_t[#idx_t + 1] = lnbyte * i + end]] + + local lnbyte, filesz, count, bufsz, idx_t = load_fixed_index(bpl, fsz, 1023) + + local file_t = setmetatable({},{__mode = "kv"}) --weak keys and values + -- this allows them to be garbage collected as space is needed + -- rebuilds when needed + local ovf = 0 + local lpos = 1 + local timer = _timer() + file:seek("set", 0) + + function hex_co() + while true do + collectgarbage("step") + file_t[1] = hstr --position 1 is ALWAYS header/title + + for i = 1, bufsz + ovf do + local pos = file:seek() + local s = file:read (lnbyte) + if not s then -- EOF + file_t[lpos + i] = "" + break; + end + local s_len = s:len() + + if s_len > 0 then + local fmt = "0x%04X: " .. string.rep("%02X ", s_len) + local schrs = " " .. s:gsub("(%c)", " . ") + file_t[lpos + i] = string.format(fmt, pos, s:byte(1, s_len)) .. + schrs + else + file_t[lpos + i] = string.format("0x%04X: ", pos) + end + end + ovf = 0 + lpos = lpos + bufsz + + local bpos = coroutine.yield() + + if bpos < lpos then -- roll over or scroll up + bpos = (bpos - bufsz) + bpos % bufsz + timer:check(true) + end + + lpos = bpos - bpos % bufsz + + if lpos < 1 then + lpos = 1 + elseif lpos > count - bufsz then -- partial fill + ovf = count - bufsz - lpos + end + --get position in file of the nearest indexed line + file:seek("set", idx_t[bpos / bufsz + 1]) + + -- on really large files if it has been more than 10 minutes + -- since the user scrolled up the screen wipe out the prior + -- items to free memory + if lpos % 10000 == 0 and timer:check() > rb.HZ * 600 then + for i = 1, lpos - 100 do + file_t[i] = nil + end + end + + end + end + + co = coroutine.create(hex_co) + + local function repl(char) + local ret = "" + if char:sub(1,2) == "0x" then + return string.format("%dd:", tonumber(char:sub(3, -2), 16)) + else + return string.format("%03d ", tonumber(char, 16)) + end + end + + + _lcd:clear() + _print.clear() + + local sel, start, vcur = 1 + table.insert(file_t, 1, hstr) --position 1 is header/title + + if not settings then + settings = {} + settings.justify = "left" + settings.wrap = true + settings.msel = false + settings.hfgc = _clr.set( 0, 000, 000, 000) + settings.hbgc = _clr.set(-1, 255, 255, 255) + settings.ifgc = _clr.set(-1, 255, 255, 255) + settings.ibgc = _clr.set( 0, 000, 000, 000) + settings.iselc = _clr.set( 1, 000, 200, 100) + end + + settings.hasheader = true + settings.co_routine = co + settings.start = start + settings.curpos = vcur + settings.ovfl = "manual" + + while sel > 0 do + settings.start = start + settings.curpos = vcur + + sel, start, vcur = print_table(file_t, count, settings) + + if sel > 1 and file_t[sel] then -- flips between hex and decimal + local s = file_t[sel] + if s:sub(-1) == "\b" then + file_t[sel] = nil + ovf = -(bufsz - 1) + coroutine.resume(co, sel) --rebuild this item + else + s = s:gsub("(0x%x+:)", repl) .. "\b" + file_t[sel] = s:gsub("(%x%x%s)", repl) .. "\b" + end + end + end + + file:close() + idx_t = nil + file_t = nil + return sel +end -- print_file_hex +-------------------------------------------------------------------------------- diff --git a/apps/plugins/lua/lua.make b/apps/plugins/lua/lua.make index 60dfd24cdd..c85182880b 100644 --- a/apps/plugins/lua/lua.make +++ b/apps/plugins/lua/lua.make @@ -16,14 +16,7 @@ LUA_OBJ := $(call c2obj, $(LUA_SRC)) OTHER_SRC += $(LUA_SRC) LUA_INCLUDEDIR := $(LUA_SRCDIR)/include_lua -LUA_INCLUDELIST := $(addprefix $(LUA_BUILDDIR)/,audio.lua blit.lua color.lua \ - draw.lua draw_floodfill.lua draw_poly.lua draw_num.lua \ - draw_text.lua files.lua image.lua image_save.lua lcd.lua \ - math_ex.lua print.lua timer.lua playlist.lua pcm.lua \ - sound.lua rbcompat.lua rbsettings.lua poly_points.lua \ - printtable.lua printmenus.lua printsubmenu.lua \ - menubuttons.lua menucoresettings.lua create_kbd_layout.lua \ - temploader.lua) +LUA_INCLUDELIST := $(wildcard $(LUA_INCLUDEDIR)/*.lua) ifndef APP_TYPE ROCKS += $(LUA_BUILDDIR)/lua.rock @@ -31,6 +24,7 @@ else ### simulator ROCKS += $(LUA_BUILDDIR)/lua.rock endif +all: $(subst $(LUA_INCLUDEDIR)/,$(LUA_BUILDDIR)/,$(LUA_INCLUDELIST)) $(LUA_BUILDDIR)/lua.rock: $(LUA_OBJ) $(TLSFLIB) $(LUA_BUILDDIR)/actions.lua $(LUA_BUILDDIR)/buttons.lua $(LUA_BUILDDIR)/settings.lua \ $(LUA_BUILDDIR)/rocklib_aux.o $(LUA_BUILDDIR)/rb_defines.lua $(LUA_BUILDDIR)/sound_defines.lua \ @@ -66,7 +60,7 @@ $(LUA_BUILDDIR)/rocklib_aux.o: $(LUA_BUILDDIR)/rocklib_aux.c $(call PRINTS,CC $(<F))$(CC) $(INCLUDES) $(PLUGINFLAGS) -I $(LUA_SRCDIR) -c $< -o $@ $(LUA_BUILDDIR)/%.lua: $(LUA_INCLUDEDIR)/%.lua | $(LUA_BUILDDIR) - $(call PRINTS,CP $(subst $(LUA_INCLUDEDIR)/,,$<))cp $< $@ + $(call PRINTS,CP $(notdir $<))cp $< $@ $(LUA_BUILDDIR)/lua.refmap: $(LUA_OBJ) $(TLSFLIB) diff --git a/apps/plugins/lua_scripts/lua_scripts.lua b/apps/plugins/lua_scripts/lua_scripts.lua new file mode 100644 index 0000000000..331611d389 --- /dev/null +++ b/apps/plugins/lua_scripts/lua_scripts.lua @@ -0,0 +1,172 @@ +--[[ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2017 William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +]] + +local scrpath = rb.current_path() + +package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path +require("printmenus") + +rb.actions = nil +package.loaded["actions"] = nil + +-------------------------------------------------------------------------------- +local Icon_Plugin = 0x9 + + +local function get_files(path, norecurse, finddir, findfile, f_t, d_t) + + local quit = false + + local files = f_t or {} + local dirs = d_t or {} + + local function f_filedir(name) + --default find function + -- example: return name:find(".mp3", 1, true) ~= nil + if name:len() <= 2 and (name == "." or name == "..") then + return false + end + if string.sub(name, 1, 1) == '.' then + return false + end + if string.sub(name, -4) == ".lua" then + return true + end + return false + end + local function d_filedir(name) + --default discard function + return false + end + + if finddir == nil then + finddir = f_filedir + elseif type(finddir) ~= "function" then + finddir = d_filedir + end + + if findfile == nil then + findfile = f_filedir + elseif type(findfile) ~= "function" then + findfile = d_filedir + end + + local function _get_files(path, cancelbtn) + local sep = "" + if string.sub(path, - 1) ~= "/" then sep = "/" end + for fname, isdir in luadir.dir(path) do + + if isdir and finddir(fname) then + table.insert(dirs, path .. sep ..fname) + elseif not isdir and findfile(fname) then + table.insert(files, path .. sep ..fname) + end + + if rb.get_plugin_action(0) == cancelbtn then + return true + end + end + end + + local function cmp_alphanum (op1, op2) + local type1= type(op1) + local type2 = type(op2) + + if type1 ~= type2 then + return type1 < type2 + else + if type1 == "string" then + op1 = op1:upper() + op2 = op2:upper() + end + return op1 < op2 + end + end + + table.insert(dirs, path) -- root + + for key,value in pairs(dirs) do + --luadir.dir may error out so we need to do the call protected + _, quit = pcall(_get_files, value, CANCEL_BUTTON) + + if quit == true or norecurse then + break; + end + end + + table.sort(files, cmp_alphanum) + table.sort(dirs, cmp_alphanum) + + return dirs, files +end -- get_files +-------------------------------------------------------------------------------- + +function icon_fn(item, icon) + if item ~= 0 then + icon = Icon_Plugin + else + icon = -1 + end + return icon +end + +-- uses print_table and get_files to display simple file browser +function script_choose(dir, title) + local dstr + local hstr = title + + local norecurse = true + local f_finddir = false -- function to match directories; nil all, false none + local f_findfile = nil -- function to match files; nil all, false none + local t_linedesc = {show_icons = true, icon_fn = icon_fn} + local p_settings = {wrap = true, hasheader = true, justify = "left", linedesc = t_linedesc} + local files = {} + local dirs = {} + local item = 1 + rb.lcd_clear_display() + + while item > 0 do + dirs, files = get_files(dir, norecurse, f_finddir, f_findfile, dirs, files) + for i=1, #dirs do dirs[i] = nil end -- empty table for reuse + table.insert(dirs, 1, hstr) + for i = 1, #files do + table.insert(dirs, "\t" .. string.gsub(files[i], ".*/","")) + end + --print_menu(menu_t, func_t, selected, settings, copy_screen) + _, item = print_menu(dirs, nil, 0, p_settings) + + -- If item was selected follow directory or return filename + item = item or -1 + if item > 0 then + dir = files[item - 1] + if not rb.dir_exists("/" .. dir) then + return dir + end + end + + end +end -- file_choose +-------------------------------------------------------------------------------- + +local script_path = script_choose(scrpath, "lua scripts") +if script_path then rb.restart_lua(script_path) end diff --git a/apps/plugins/lua_scripts/lua_scripts.make b/apps/plugins/lua_scripts/lua_scripts.make index 2f46f9d74a..9151ab6d1c 100644 --- a/apps/plugins/lua_scripts/lua_scripts.make +++ b/apps/plugins/lua_scripts/lua_scripts.make @@ -10,7 +10,6 @@ LUASCR_SRCDIR := $(APPSDIR)/plugins/lua_scripts LUASCR_BUILDDIR := $(BUILDDIR)/apps/plugins/lua_scripts LUASCRS := $(wildcard $(LUASCR_SRCDIR)/*.lua) - #DUMMY := $(info [${LUASCRS}]) DUMMY : all @@ -18,7 +17,6 @@ DUMMY : all all: $(subst $(LUASCR_SRCDIR)/,$(LUASCR_BUILDDIR)/,$(LUASCRS)) $(LUASCR_BUILDDIR)/%.lua: $(LUASCR_SRCDIR)/%.lua | $(LUASCR_BUILDDIR) - $(call PRINTS,CP $(subst $(LUASCR_SRCDIR)/,,$<))cp $< $@ - + $(call PRINTS,CP $(subst $(APPSDIR)/,,$<))cp $< $@ $(LUASCR_BUILDDIR): $(call PRINTS,MKDIR $@)mkdir -p $(LUASCR_BUILDDIR)/ diff --git a/apps/plugins/picross.lua b/apps/plugins/picross.lua index 26ad57ee74..ec9b6ef7a9 100644 --- a/apps/plugins/picross.lua +++ b/apps/plugins/picross.lua @@ -24,7 +24,7 @@ local _clr = require("color") -- clrset, clrinc provides device independent co local _lcd = require("lcd") -- lcd helper functions local plugindir = rb.PLUGIN_GAMES_DATA_DIR -local userdir = plugindir .. "/picross" +local userdir = plugindir .. "/.picross" local wrap = rb.settings.read('global_settings', rb.system.global_settings.list_wraparound) wrap = (wrap or 1) == 1 @@ -371,7 +371,7 @@ function State:loadSave() end function State:loadDefault() - self:loadFile(userdir .. '/picross_default.picross') + return self:loadFile(userdir .. '/picross_default.picross') end function State:loadFile(path) @@ -763,7 +763,9 @@ function viewPicture() end if not State:loadSave() then - State:loadDefault() + if not State:loadDefault() then + return; + end end local act = rb.actions diff --git a/apps/plugins/picross/picross.make b/apps/plugins/picross/picross.make index d34b251dac..ead5ba482a 100644 --- a/apps/plugins/picross/picross.make +++ b/apps/plugins/picross/picross.make @@ -8,7 +8,7 @@ # PICRSCR_SRCDIR := $(APPSDIR)/plugins/picross -PICRSCR_BUILDDIR := $(BUILDDIR)/apps/plugins/picross +PICRSCR_BUILDDIR := $(BUILDDIR)/apps/plugins/.picross PICRSCRS := $(wildcard $(PICRSCR_SRCDIR)/*.picross) #DUMMY := $(info [${PICRSCRS}]) @@ -18,7 +18,7 @@ DUMMY : all all: $(subst $(PICRSCR_SRCDIR)/,$(PICRSCR_BUILDDIR)/,$(PICRSCRS)) $(PICRSCR_BUILDDIR)/%.picross: $(PICRSCR_SRCDIR)/%.picross | $(PICRSCR_BUILDDIR) - $(call PRINTS,CP $(subst $(PICRSCR_SRCDIR)/,,$<))cp $< $@ + $(call PRINTS,CP $(subst $(APPSDIR)/,,$<))cp $< $@ $(PICRSCR_BUILDDIR): $(call PRINTS,MKDIR $@)mkdir -p $(PICRSCR_BUILDDIR)/ diff --git a/apps/settings.h b/apps/settings.h index 2277805fec..e3b11430cd 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -163,7 +163,7 @@ enum { SHOW_ALL, SHOW_SUPPORTED, SHOW_MUSIC, SHOW_PLAYLIST, SHOW_ID3DB, /* file and dir sort options */ enum { SORT_ALPHA, SORT_DATE, SORT_DATE_REVERSED, SORT_TYPE, /* available as settings */ - SORT_ALPHA_REVERSED, SORT_TYPE_REVERSED }; /* internal use only */ + SORT_ALPHA_REVERSED, SORT_TYPE_REVERSED, SORT_AS_FILE }; /* internal use only */ enum { SORT_INTERPRET_AS_DIGIT, SORT_INTERPRET_AS_NUMBER }; /* recursive dir insert options */ diff --git a/apps/tree.c b/apps/tree.c index 2e82b165af..58457c2d71 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -466,6 +466,13 @@ static int update_dir(void) icon = tc.browse->icon; if (icon == NOICON) icon = filetype_get_icon(ATTR_DIRECTORY); + /* display sub directories in the title of plugin browser */ + if (tc.dirlevel > 0 && *tc.dirfilter == SHOW_PLUGINS) + { + char *subdir = strrchr(tc.currdir, '/'); + if (subdir) + title = subdir + 1; /* step past the separator */ + } } else { @@ -1056,7 +1063,7 @@ int rockbox_browse(struct browse_context *browse) int last_context; /* don't reset if its the same browse already loaded */ if (tc.browse != browse || - !(tc.currdir[1] && strcmp(tc.currdir, browse->root) == 0)) + !(tc.currdir[1] && strstr(tc.currdir, browse->root) != NULL)) { tc.browse = browse; tc.selected_item = 0; diff --git a/tools/buildzip.pl b/tools/buildzip.pl index 2956b49492..59c32d3e3d 100755 --- a/tools/buildzip.pl +++ b/tools/buildzip.pl @@ -167,6 +167,13 @@ sub make_install { glob_install("$src/rocks/$t/*", "$libdir/rocks/$t", "-m 0755"); } + if(-e "$src/rocks/games/sgt_puzzles") { + unless (glob_mkdir("$libdir/rocks/games/sgt_puzzles")) { + return 0; + } + glob_install("$src/rocks/games/sgt_puzzles/*", "$libdir/rocks/games/sgt_puzzles", "-m 0755"); + } + # rocks/viewers/lua unless (glob_mkdir("$libdir/rocks/viewers/lua")) { return 0; @@ -182,12 +189,13 @@ sub make_install { #glob_mkdir("$temp_dir/rocks/demos/lua_scripts"); #glob_copy("$ROOT/apps/plugins/lua_scripts/*.lua", "$temp_dir/rocks/demos/lua_scripts/"); } - #lua picross puzzles + + #lua picross puzzles if(-e "$ROOT/apps/plugins/picross") { - unless (glob_mkdir("$libdir/rocks/games/picross")) { + unless (glob_mkdir("$libdir/rocks/games/.picross")) { return 0; } - glob_install("$ROOT/apps/plugins/picross/*.picross", "$libdir/rocks/games/picross"); + glob_install("$ROOT/apps/plugins/picross/*.picross", "$libdir/rocks/games/.picross"); } # all the rest directories @@ -451,6 +459,12 @@ sub buildzip { glob_copy("$ROOT/apps/plugins/lua_scripts/*.lua", "$temp_dir/rocks/demos/lua_scripts/"); } + #lua picross puzzles + if(-e "$ROOT/apps/plugins/picross") { + glob_mkdir("$temp_dir/rocks/games/.picross"); + glob_copy("$ROOT/apps/plugins/picross/*.picross", "$temp_dir/rocks/games/.picross/"); + } + # exclude entries for the image file types not supported by the imageviewer for the target. my $viewers = "$ROOT/apps/plugins/viewers.config"; my $c="cat $viewers | gcc $cppdef -I. -I$firmdir/export -E -P -include config.h -"; @@ -512,7 +526,13 @@ sub buildzip { foreach my $line (@rock_targetdirs) { if ($line =~ /([^,]*),(.*)/) { my ($plugin, $dir)=($1, $2); - move("$temp_dir/rocks/${plugin}.rock", "$temp_dir/rocks/$dir/${plugin}.rock"); + if($dir eq 'games' and substr(${plugin}, 0, 4) eq "sgt-") { + glob_mkdir("$temp_dir/rocks/$dir/sgt_puzzles"); + move("$temp_dir/rocks/${plugin}.rock", "$temp_dir/rocks/$dir/sgt_puzzles/${plugin}.rock"); + } + else { + move("$temp_dir/rocks/${plugin}.rock", "$temp_dir/rocks/$dir/${plugin}.rock"); + } if(-e "$temp_dir/rocks/${plugin}.ovl") { # if there's an "overlay" file for the .rock, move that as # well |