Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[ tape program, provides basic tape modification and access tools
- Authors: Bizzycola and Vexatos
- ]]
- local component = require("component")
- local fs = require("filesystem")
- local shell = require("shell")
- local term = require("term")
- local args, options = shell.parse(...)
- if not component.isAvailable("tape_drive") then
- io.stderr:write("This program requires a tape drive to run.")
- return
- end
- local function printUsage()
- print("Usage:")
- print(" - 'tape play' to start playing a tape")
- print(" - 'tape pause' to pause playing the tape")
- print(" - 'tape stop' to stop playing and rewind the tape")
- print(" - 'tape rewind' to rewind the tape")
- print(" - 'tape wipe' to wipe any data on the tape and erase it completely")
- print(" - 'tape label [name]' to label the tape, leave 'name' empty to get current label")
- print(" - 'tape speed <speed>' to set the playback speed. Needs to be between 0.25 and 2.0")
- print(" - 'tape volume <volume>' to set the volume of the tape. Needs to be between 0.0 and 1.0")
- print(" - 'tape write <path/of/audio/file>' to write to the tape from a file")
- print(" - 'tape write <URL>' to write from a URL")
- print("Other options:")
- print(" '--address=<address>' to use a specific tape drive")
- print(" '--b=<bytes>' to specify the size of the chunks the program will write to a tape")
- print(" '--t=<timeout>' to specify a custom maximum timeout in seconds when writing from a URL")
- print(" '-y' to not ask for confirmation before starting to write")
- return
- end
- local function getTapeDrive()
- --Credits to gamax92 for this
- local tape
- if options.address then
- if type(options.address) ~= "string" then
- io.stderr:write("'address' may only be a string.")
- return
- end
- local fulladdr = component.get(options.address)
- if fulladdr == nil then
- io.stderr:write("No component at this address.")
- return
- end
- if component.type(fulladdr) ~= "tape_drive" then
- io.stderr:write("No tape drive at this address.")
- return
- end
- tape = component.proxy(fulladdr)
- else
- tape = component.tape_drive
- end
- return tape
- --End of gamax92's part
- end
- local tape = getTapeDrive()
- if not tape.isReady() then
- io.stderr:write("The tape drive does not contain a tape.")
- return
- end
- local function label(name)
- if not name then
- if tape.getLabel() == "" then
- print("Tape is currently not labeled.")
- return
- end
- print("Tape is currently labeled: " .. tape.getLabel())
- return
- end
- tape.setLabel(name)
- print("Tape label set to " .. name)
- end
- local function rewind()
- print("Rewound tape")
- tape.seek(-tape.getSize())
- end
- local function play()
- if tape.getState() == "PLAYING" then
- print("Tape is already playing")
- else
- tape.play()
- print("Tape started")
- end
- end
- local function stop()
- if tape.getState() == "STOPPED" then
- print("Tape is already stopped")
- else
- tape.stop()
- tape.seek(-tape.getSize())
- print("Tape stopped")
- end
- end
- local function pause()
- if tape.getState() == "STOPPED" then
- print("Tape is already paused")
- else
- tape.stop()
- print("Tape paused")
- end
- end
- local function speed(sp)
- local s = tonumber(sp)
- if not s or s < 0.25 or s > 2 then
- io.stderr:write("Speed needs to be a number between 0.25 and 2.0")
- return
- end
- tape.setSpeed(s)
- print("Playback speed set to " .. sp)
- end
- local function volume(vol)
- local v = tonumber(vol)
- if not v or v < 0 or v > 1 then
- io.stderr:write("Volume needs to be a number between 0.0 and 1.0")
- return
- end
- tape.setVolume(v)
- print("Volume set to " .. vol)
- end
- local function confirm(msg)
- if not options.y then
- print(msg)
- print("Type `y` to confirm, `n` to cancel.")
- repeat
- local response = io.read()
- if response and response:lower():sub(1, 1) == "n" then
- print("Canceled.")
- return false
- end
- until response and response:lower():sub(1, 1) == "y"
- end
- return true
- end
- local function wipe()
- if not confirm("Are you sure you want to wipe this tape?") then return end
- local k = tape.getSize()
- tape.stop()
- tape.seek(-k)
- tape.stop() --Just making sure
- tape.seek(-90000)
- local s = string.rep("\xAA", 8192)
- for i = 1, k + 8191, 8192 do
- tape.write(s)
- end
- tape.seek(-k)
- tape.seek(-90000)
- print("Done.")
- end
- local function writeTape(path)
- local file, msg, _, y
- local block = 2048 --How much to read at a time
- if options.b then
- local nBlock = tonumber(options.b)
- if nBlock then
- print("Setting chunk size to " .. options.b)
- block = nBlock
- else
- io.stderr:write("option --b is not a number.\n")
- return
- end
- end
- if not confirm("Are you sure you want to write to this tape?") then return end
- tape.stop()
- tape.seek(-tape.getSize())
- tape.stop() --Just making sure
- local bytery = 0 --For the progress indicator
- local filesize = tape.getSize()
- if string.match(path, "https?://.+") then
- if not component.isAvailable("internet") then
- io.stderr:write("This command requires an internet card to run.")
- return false
- end
- local internet = component.internet
- local function setupConnection(url)
- local file, reason = internet.request(url)
- if not file then
- io.stderr:write("error requesting data from URL: " .. reason .. "\n")
- return false
- end
- local connected, reason = false, ""
- local timeout = 50
- if options.t then
- local nTimeout = tonumber(options.t)
- if nTimeout then
- print("Max timeout: " .. options.t)
- timeout = nTimeout * 10
- else
- io.stderr:write("option --t is not a number. Defaulting to 5 seconds.\n")
- end
- end
- for i = 1, timeout do
- connected, reason = file.finishConnect()
- os.sleep(.1)
- if connected or connected == nil then
- break
- end
- end
- if connected == nil then
- io.stderr:write("Could not connect to server: " .. reason)
- return false
- end
- local status, message, header = file.response()
- if status then
- status = string.format("%d", status)
- print("Status: " .. status .. " " .. message)
- if status:sub(1,1) == "2" then
- return true, {
- close = function(self, ...) return file.close(...) end,
- read = function(self, ...) return file.read(...) end,
- }, header
- end
- return false
- end
- io.stderr:write("no valid HTTP response - no response")
- return false
- end
- local success, header
- success, file, header = setupConnection(path)
- if not success then
- if file then
- file:close()
- end
- return
- end
- print("Writing...")
- _, y = term.getCursor()
- if header and header["Content-Length"] and header["Content-Length"][1] then
- filesize = tonumber(header["Content-Length"][1])
- end
- else
- local path = shell.resolve(path)
- filesize = fs.size(path)
- print("Path: " .. path)
- file, msg = io.open(path, "rb")
- if not file then
- io.stderr:write("Error: " .. msg)
- return
- end
- print("Writing...")
- _, y = term.getCursor()
- end
- if filesize > tape.getSize() then
- term.setCursor(1, y)
- io.stderr:write("Warning: File is too large for tape, shortening file\n")
- _, y = term.getCursor()
- filesize = tape.getSize()
- end
- --Displays long numbers with commas
- local function fancyNumber(n)
- return tostring(math.floor(n)):reverse():gsub("(%d%d%d)", "%1,"):gsub("%D$", ""):reverse()
- end
- repeat
- local bytes = file:read(block)
- if bytes and #bytes > 0 then
- if not tape.isReady() then
- io.stderr:write("\nError: Tape was removed during writing.\n")
- file:close()
- return
- end
- term.setCursor(1, y)
- bytery = bytery + #bytes
- local displaySize = math.min(bytery, filesize)
- term.write(string.format("Read %s of %s bytes... (%.2f %%)", fancyNumber(displaySize), fancyNumber(filesize), 100 * displaySize / filesize))
- tape.write(bytes)
- end
- until not bytes or bytery > filesize
- file:close()
- tape.stop()
- tape.seek(-tape.getSize())
- tape.stop() --Just making sure
- print("\nDone.")
- end
- if args[1] == "play" then
- play()
- elseif args[1] == "stop" then
- stop()
- elseif args[1] == "pause" then
- pause()
- elseif args[1] == "rewind" then
- rewind()
- elseif args[1] == "label" then
- label(args[2])
- elseif args[1] == "speed" then
- speed(args[2])
- elseif args[1] == "volume" then
- volume(args[2])
- elseif args[1] == "write" then
- writeTape(args[2])
- elseif args[1] == "wipe" then
- wipe()
- else
- printUsage()
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement