Advertisement
DarkProDEn

tape.lua

Jan 6th, 2022
16
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.84 KB | None | 0 0
  1. --[[ tape program, provides basic tape modification and access tools
  2. Authors: Bizzycola and Vexatos
  3. ]]
  4. local component = require("component")
  5. local fs = require("filesystem")
  6. local shell = require("shell")
  7. local term = require("term")
  8.  
  9. local args, options = shell.parse(...)
  10.  
  11. if not component.isAvailable("tape_drive") then
  12. io.stderr:write("This program requires a tape drive to run.")
  13. return
  14. end
  15.  
  16. local function printUsage()
  17. print("Usage:")
  18. print(" - 'tape play' to start playing a tape")
  19. print(" - 'tape pause' to pause playing the tape")
  20. print(" - 'tape stop' to stop playing and rewind the tape")
  21. print(" - 'tape rewind' to rewind the tape")
  22. print(" - 'tape wipe' to wipe any data on the tape and erase it completely")
  23. print(" - 'tape label [name]' to label the tape, leave 'name' empty to get current label")
  24. print(" - 'tape speed <speed>' to set the playback speed. Needs to be between 0.25 and 2.0")
  25. print(" - 'tape volume <volume>' to set the volume of the tape. Needs to be between 0.0 and 1.0")
  26. print(" - 'tape write <path/of/audio/file>' to write to the tape from a file")
  27. print(" - 'tape write <URL>' to write from a URL")
  28. print("Other options:")
  29. print(" '--address=<address>' to use a specific tape drive")
  30. print(" '--b=<bytes>' to specify the size of the chunks the program will write to a tape")
  31. print(" '--t=<timeout>' to specify a custom maximum timeout in seconds when writing from a URL")
  32. print(" '-y' to not ask for confirmation before starting to write")
  33. return
  34. end
  35.  
  36. local function getTapeDrive()
  37. --Credits to gamax92 for this
  38. local tape
  39. if options.address then
  40. if type(options.address) ~= "string" then
  41. io.stderr:write("'address' may only be a string.")
  42. return
  43. end
  44. local fulladdr = component.get(options.address)
  45. if fulladdr == nil then
  46. io.stderr:write("No component at this address.")
  47. return
  48. end
  49. if component.type(fulladdr) ~= "tape_drive" then
  50. io.stderr:write("No tape drive at this address.")
  51. return
  52. end
  53. tape = component.proxy(fulladdr)
  54. else
  55. tape = component.tape_drive
  56. end
  57. return tape
  58. --End of gamax92's part
  59. end
  60.  
  61. local tape = getTapeDrive()
  62.  
  63. if not tape.isReady() then
  64. io.stderr:write("The tape drive does not contain a tape.")
  65. return
  66. end
  67.  
  68. local function label(name)
  69. if not name then
  70. if tape.getLabel() == "" then
  71. print("Tape is currently not labeled.")
  72. return
  73. end
  74. print("Tape is currently labeled: " .. tape.getLabel())
  75. return
  76. end
  77. tape.setLabel(name)
  78. print("Tape label set to " .. name)
  79. end
  80.  
  81. local function rewind()
  82. print("Rewound tape")
  83. tape.seek(-tape.getSize())
  84. end
  85.  
  86. local function play()
  87. if tape.getState() == "PLAYING" then
  88. print("Tape is already playing")
  89. else
  90. tape.play()
  91. print("Tape started")
  92. end
  93. end
  94.  
  95. local function stop()
  96. if tape.getState() == "STOPPED" then
  97. print("Tape is already stopped")
  98. else
  99. tape.stop()
  100. tape.seek(-tape.getSize())
  101. print("Tape stopped")
  102. end
  103. end
  104.  
  105. local function pause()
  106. if tape.getState() == "STOPPED" then
  107. print("Tape is already paused")
  108. else
  109. tape.stop()
  110. print("Tape paused")
  111. end
  112. end
  113.  
  114. local function speed(sp)
  115. local s = tonumber(sp)
  116. if not s or s < 0.25 or s > 2 then
  117. io.stderr:write("Speed needs to be a number between 0.25 and 2.0")
  118. return
  119. end
  120. tape.setSpeed(s)
  121. print("Playback speed set to " .. sp)
  122. end
  123.  
  124. local function volume(vol)
  125. local v = tonumber(vol)
  126. if not v or v < 0 or v > 1 then
  127. io.stderr:write("Volume needs to be a number between 0.0 and 1.0")
  128. return
  129. end
  130. tape.setVolume(v)
  131. print("Volume set to " .. vol)
  132. end
  133.  
  134. local function confirm(msg)
  135. if not options.y then
  136. print(msg)
  137. print("Type `y` to confirm, `n` to cancel.")
  138. repeat
  139. local response = io.read()
  140. if response and response:lower():sub(1, 1) == "n" then
  141. print("Canceled.")
  142. return false
  143. end
  144. until response and response:lower():sub(1, 1) == "y"
  145. end
  146. return true
  147. end
  148.  
  149. local function wipe()
  150. if not confirm("Are you sure you want to wipe this tape?") then return end
  151. local k = tape.getSize()
  152. tape.stop()
  153. tape.seek(-k)
  154. tape.stop() --Just making sure
  155. tape.seek(-90000)
  156. local s = string.rep("\xAA", 8192)
  157. for i = 1, k + 8191, 8192 do
  158. tape.write(s)
  159. end
  160. tape.seek(-k)
  161. tape.seek(-90000)
  162. print("Done.")
  163. end
  164.  
  165. local function writeTape(path)
  166. local file, msg, _, y
  167. local block = 2048 --How much to read at a time
  168. if options.b then
  169. local nBlock = tonumber(options.b)
  170. if nBlock then
  171. print("Setting chunk size to " .. options.b)
  172. block = nBlock
  173. else
  174. io.stderr:write("option --b is not a number.\n")
  175. return
  176. end
  177. end
  178. if not confirm("Are you sure you want to write to this tape?") then return end
  179. tape.stop()
  180. tape.seek(-tape.getSize())
  181. tape.stop() --Just making sure
  182.  
  183. local bytery = 0 --For the progress indicator
  184. local filesize = tape.getSize()
  185.  
  186. if string.match(path, "https?://.+") then
  187.  
  188. if not component.isAvailable("internet") then
  189. io.stderr:write("This command requires an internet card to run.")
  190. return false
  191. end
  192.  
  193. local internet = component.internet
  194.  
  195. local function setupConnection(url)
  196.  
  197. local file, reason = internet.request(url)
  198.  
  199. if not file then
  200. io.stderr:write("error requesting data from URL: " .. reason .. "\n")
  201. return false
  202. end
  203.  
  204. local connected, reason = false, ""
  205. local timeout = 50
  206. if options.t then
  207. local nTimeout = tonumber(options.t)
  208. if nTimeout then
  209. print("Max timeout: " .. options.t)
  210. timeout = nTimeout * 10
  211. else
  212. io.stderr:write("option --t is not a number. Defaulting to 5 seconds.\n")
  213. end
  214. end
  215. for i = 1, timeout do
  216. connected, reason = file.finishConnect()
  217. os.sleep(.1)
  218. if connected or connected == nil then
  219. break
  220. end
  221. end
  222.  
  223. if connected == nil then
  224. io.stderr:write("Could not connect to server: " .. reason)
  225. return false
  226. end
  227.  
  228. local status, message, header = file.response()
  229.  
  230. if status then
  231. status = string.format("%d", status)
  232. print("Status: " .. status .. " " .. message)
  233. if status:sub(1,1) == "2" then
  234. return true, {
  235. close = function(self, ...) return file.close(...) end,
  236. read = function(self, ...) return file.read(...) end,
  237. }, header
  238. end
  239. return false
  240. end
  241. io.stderr:write("no valid HTTP response - no response")
  242. return false
  243. end
  244.  
  245. local success, header
  246. success, file, header = setupConnection(path)
  247. if not success then
  248. if file then
  249. file:close()
  250. end
  251. return
  252. end
  253.  
  254. print("Writing...")
  255.  
  256. _, y = term.getCursor()
  257.  
  258. if header and header["Content-Length"] and header["Content-Length"][1] then
  259. filesize = tonumber(header["Content-Length"][1])
  260. end
  261. else
  262. local path = shell.resolve(path)
  263. filesize = fs.size(path)
  264. print("Path: " .. path)
  265. file, msg = io.open(path, "rb")
  266. if not file then
  267. io.stderr:write("Error: " .. msg)
  268. return
  269. end
  270.  
  271. print("Writing...")
  272.  
  273. _, y = term.getCursor()
  274. end
  275.  
  276. if filesize > tape.getSize() then
  277. term.setCursor(1, y)
  278. io.stderr:write("Warning: File is too large for tape, shortening file\n")
  279. _, y = term.getCursor()
  280. filesize = tape.getSize()
  281. end
  282.  
  283. --Displays long numbers with commas
  284. local function fancyNumber(n)
  285. return tostring(math.floor(n)):reverse():gsub("(%d%d%d)", "%1,"):gsub("%D$", ""):reverse()
  286. end
  287.  
  288. repeat
  289. local bytes = file:read(block)
  290. if bytes and #bytes > 0 then
  291. if not tape.isReady() then
  292. io.stderr:write("\nError: Tape was removed during writing.\n")
  293. file:close()
  294. return
  295. end
  296. term.setCursor(1, y)
  297. bytery = bytery + #bytes
  298. local displaySize = math.min(bytery, filesize)
  299. term.write(string.format("Read %s of %s bytes... (%.2f %%)", fancyNumber(displaySize), fancyNumber(filesize), 100 * displaySize / filesize))
  300. tape.write(bytes)
  301. end
  302. until not bytes or bytery > filesize
  303. file:close()
  304. tape.stop()
  305. tape.seek(-tape.getSize())
  306. tape.stop() --Just making sure
  307. print("\nDone.")
  308. end
  309.  
  310. if args[1] == "play" then
  311. play()
  312. elseif args[1] == "stop" then
  313. stop()
  314. elseif args[1] == "pause" then
  315. pause()
  316. elseif args[1] == "rewind" then
  317. rewind()
  318. elseif args[1] == "label" then
  319. label(args[2])
  320. elseif args[1] == "speed" then
  321. speed(args[2])
  322. elseif args[1] == "volume" then
  323. volume(args[2])
  324. elseif args[1] == "write" then
  325. writeTape(args[2])
  326. elseif args[1] == "wipe" then
  327. wipe()
  328. else
  329. printUsage()
  330. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement