Something went wrong. Try again.
A modern, network-enabled music player platform built on Rockbox technology. rockboxd.tsiry-sandratraina.com
rust deno navidrome airplay libadwaita zig mpris snapcast mpd rockbox audio subsonic
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468--[[/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * * 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'") endrequire("printtable")local _clr = require("color")local _lcd = require("lcd")local _print = require("print")local _timer = require("timer")
require("actions")_G.CANCEL_BUTTON = rb.actions.PLA_CANCEL --global ---------------------------------------------------------------------------------- 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 binarylocal 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") endend -- build_file_index--------------------------------------------------------------------------------
--- returns size of original file, total lines buffersize, and table filled-- with line offsets in index file -> filenamelocal 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, ltableend -- 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 fallbacklocal 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_tend -- load_fixed_index--------------------------------------------------------------------------------
-- uses print_table to display a whole filefunction 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 = nilend -- print_file--------------------------------------------------------------------------------
-- uses print_table to display a portion of a filefunction 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 selend --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 selend -- print_file_hex--------------------------------------------------------------------------------