local drive = peripheral.find("tape_drive") if not drive then printError("no tape drive found") printError("it's kinda required to play tapes, you know?") return end local running = true term.setBackgroundColor(colors.black) term.clear() local screen_w, screen_h = term.getSize() local table_of_contents = {} local function read32() local v = 0 for i = 1, 4 do local b = drive.read() v = bit32.bor(bit32.lshift(v, 8), b) end return v end local function bytes2time(b) local s = math.floor(b / 6000) if s < 60 then return string.format("%ds", s) end return string.format("%dm, %ds", math.floor(s / 60), s % 60) end local function textProgress(p, c1, c2, fmt, ...) local tw = term.getSize() local str = string.format(fmt, ...) local w1 = math.ceil(p * tw) local w2 = tw - w1 local bg = term.getBackgroundColor() term.setBackgroundColor(c1) term.write(str:sub(1, w1)) local rem = w1 - #str if rem > 0 then term.write(string.rep(" ", rem)) end term.setBackgroundColor(c2) term.write(str:sub(w1 + 1, w1 + w2)) rem = math.min(tw - #str, w2) if rem > 0 then term.write(string.rep(" ", rem)) end term.setBackgroundColor(bg) end parallel.waitForAll( function() while running do if drive.isReady() then local pos, size = drive.getPosition(), drive.getSize() for i = 1, math.min(screen_h - 2, 48) do term.setCursorPos(1, i) local song = table_of_contents[i] if song then local is_playing = pos >= song.offset and pos < song.ending local s = string.format("#%2d %9s %s", i, bytes2time(song.length), song.title) if is_playing then local p = (pos - song.offset) / song.length textProgress(p, colors.lime, colors.lightGray, s) else term.setBackgroundColor(i % 2 == 0 and colors.gray or colors.black) term.clearLine() term.write(s) end end end term.setCursorPos(1, screen_h) textProgress(pos / size, colors.red, colors.gray, "%8d / %8d [%s]", pos, size, drive.getState()) end os.sleep(0.1) end end, function() while running do local evd = { os.pullEvent() } local ev, evd = table.remove(evd, 1), evd if ev == "mouse_click" then local x, y = table.unpack(evd, 2) if drive.isReady() and y <= #table_of_contents then drive.seek(-drive.getSize()) drive.seek(table_of_contents[y].offset) drive.play() end elseif ev == "term_resize" then term.setBackgroundColor(colors.black) term.clear() screen_w, screen_h = term.getSize() elseif ev == "tape_present" then table_of_contents = {} term.clear() if evd[1] then drive.stop() drive.seek(-drive.getSize()) for i = 1, 48 do local offset = read32() local length = read32() local title = drive.read(117):gsub("\x00", "") if length > 0 then table.insert(table_of_contents, { title = title, offset = offset, length = length, ending = offset + length }) end end end end end end, function() local tape_was_present = nil local drive_old_state = nil while running do local tape_present = drive.isReady() if tape_present ~= tape_was_present then os.queueEvent("tape_present", tape_present) tape_was_present = tape_present end local drive_state = drive.getState() if drive_old_state ~= drive_state then os.queueEvent("drive_state", drive_state) drive_old_state = drive_state end os.sleep(0.25) end end)