diff --git a/src/main/resources/assets/audio_perf/loot/tape/usr/bin/tape.lua b/src/main/resources/assets/audio_perf/loot/tape/usr/bin/tape.lua new file mode 100644 index 0000000..1d7dd39 --- /dev/null +++ b/src/main/resources/assets/audio_perf/loot/tape/usr/bin/tape.lua @@ -0,0 +1,330 @@ +--[[ 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 ' to set the playback speed. Needs to be between 0.25 and 2.0") + print(" - 'tape volume ' to set the volume of the tape. Needs to be between 0.0 and 1.0") + print(" - 'tape write ' to write to the tape from a file") + print(" - 'tape write ' to write from a URL") + print("Other options:") + print(" '--address=
' to use a specific tape drive") + print(" '--b=' to specify the size of the chunks the program will write to a tape") + print(" '--t=' 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