mirror of
				https://github.com/LDDestroier/CC/
				synced 2025-10-30 23:12:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			387 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| -- Mtape
 | |
| -- tape managing program
 | |
| -- made by LDDestroier
 | |
| 
 | |
| local _DEBUG = false
 | |
| 
 | |
| local function checkOption(argName, argInfo, isShort)
 | |
|     for i = 1, #argInfo do
 | |
|         if argInfo[i][isShort and 2 or 3] == argName then
 | |
|             return i
 | |
|         end
 | |
|     end
 | |
|     return false
 | |
| end
 | |
| 
 | |
| local function argParse(argInput, argInfo)
 | |
|     local sDelim = "-"
 | |
|     local lDelim = "--"
 | |
| 
 | |
|     local optOutput = {}
 | |
|     local argOutput = {}
 | |
|     local argError = {}
 | |
| 
 | |
|     local usedTokens = {}
 | |
| 
 | |
|     local lOpt, sOpt, optNum
 | |
| 
 | |
|     for i = 1, #argInfo do
 | |
|         optOutput[i] = {}
 | |
|     end
 | |
| 
 | |
|     for i = 1, #argInput do
 | |
|         lOpt = argInput[i]
 | |
|         -- check type of delimiter
 | |
|         if lOpt:sub(1, #lDelim) == lDelim then
 | |
|             -- handle long delimiter
 | |
|             lOpt = lOpt:sub(#lDelim + 1)
 | |
|             optNum = checkOption(lOpt, argInfo, false)
 | |
|             if optNum then
 | |
|                 if argInfo[optNum][1] == 0 then
 | |
|                     optOutput[optNum] = true
 | |
|                 
 | |
|                 else
 | |
|                     optOutput[optNum] = {}
 | |
|                     for ii = 1, argInfo[optNum][1] do
 | |
|                         if argInput[i + ii] then
 | |
|                             optOutput[optNum][#optOutput[optNum] + 1] = argInput[i + ii]
 | |
|                             usedTokens[i + ii] = true
 | |
|                         else
 | |
|                             argError[#argError + 1] = "expected parameter " .. tostring(ii) .. " for argument " .. lDelim .. lOpt
 | |
|                             break
 | |
|                         end
 | |
|                     end
 | |
|                     i = i + argInfo[optNum][1]
 | |
|                 end
 | |
| 
 | |
|             else
 | |
|                 argError[#argError + 1] = "invalid argument " .. lDelim .. lOpt
 | |
|             end
 | |
| 
 | |
| 
 | |
|         elseif lOpt:sub(1, #sDelim) == sDelim then
 | |
|             -- handle short delimiter
 | |
|             lOpt = lOpt:sub(#sDelim + 1)
 | |
|             for si = 1, #lOpt do
 | |
|                 sOpt = lOpt:sub(si, si)
 | |
|                 optNum = checkOption(sOpt, argInfo, true)
 | |
|                 if optNum then
 | |
|                     if argInfo[optNum][1] == 0 then
 | |
|                         optOutput[optNum] = true
 | |
|                     
 | |
|                     elseif si == #lOpt then
 | |
|                         optOutput[optNum] = {}
 | |
|                         for ii = 1, argInfo[optNum][1] do
 | |
|                             if argInput[i + ii] then
 | |
|                                 optOutput[optNum][#optOutput[optNum] + 1] = argInput[i + ii]
 | |
|                                 usedTokens[i + ii] = true
 | |
|                             else
 | |
|                                 argError[#argError + 1] = "expected parameter " .. tostring(ii) .. " for argument " .. sDelim .. sOpt
 | |
|                                 break
 | |
|                             end
 | |
|                         end
 | |
|                         i = i + argInfo[optNum][1]
 | |
|                     else
 | |
|                         argError[#argError + 1] = "options with parameters must be at the end of their group"
 | |
|                         break
 | |
|                     end
 | |
|                 else
 | |
|                     argError[#argError + 1] = "invalid argument " .. sDelim .. sOpt
 | |
|                     break
 | |
|                 end
 | |
|                 
 | |
|             end
 | |
| 
 | |
|         elseif not usedTokens[i] then
 | |
|             argOutput[#argOutput + 1] = lOpt
 | |
|         end
 | |
|     end
 | |
| 
 | |
|     return argOutput, optOutput, argError
 | |
| end
 | |
| 
 | |
| 
 | |
| local function getHelp(specify)
 | |
|     if not specify then
 | |
|         print("mtape [file / url] (label)")
 | |
|         print("mtape [-i --info]")
 | |
|         print("mtape [-e --erase]")
 | |
|         print("mtape [-u --unclean]")
 | |
|         print("mtape [-c --cc-media]")
 | |
|         print("mtape [-u]")
 | |
|         print("Use -h with other options for details")
 | |
|     else
 | |
|         if specify == "--info" then
 | |
|             print("Prints information about the connected tape drive.")
 | |
|             print("This includes the label, size, minutes length, and current seeking position.")
 | |
|         elseif specify == "--erase" then
 | |
|             print("Erases the connected tape drive.")
 | |
|         elseif specify == "--unclean" then
 | |
|             print("Normally, tapes are erased before writing them anew. Use this option to not erase them first.")
 | |
|             print("If the tape has something longer written to it beforehand, you'll hear it after whatever is written now.")
 | |
|         elseif specify == "--ldd-github" then
 | |
|             print("Adds URL info from LDDestroier's personal CC-Media github to make it easier to pull from there.")
 | |
|             print("Ex. 'tapey -c krabii'")
 | |
|             print("    'tapey -c simple'")
 | |
|         elseif specify == "--reinstall" then
 | |
|             print("Updates Mtape to the latest version on the LDDestroier/CC github.")
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| local tierInfo = {
 | |
|     -- damage value indicates type of tape
 | |
|     -- value of tierInfo indicates minutes of storage
 | |
|     [0] = 4,
 | |
|     [1] = 8,
 | |
|     [2] = 16,
 | |
|     [3] = 32,
 | |
|     [4] = 64,
 | |
|     [5] = 2,
 | |
|     [6] = 6,
 | |
|     [8] = 128
 | |
| }
 | |
| 
 | |
| local function getFileContents(path, isURL)
 | |
|     local file, contents
 | |
|     if isURL then
 | |
|         file = http.get(path, nil, true)
 | |
|     else
 | |
|         file = fs.open(fs.combine(shell.dir(), path), "rb")
 | |
|     end
 | |
|     if not file then
 | |
|         return false, ""
 | |
|     else
 | |
|         contents = file.readAll()
 | |
|         file.close()
 | |
|         return true, contents
 | |
|     end
 | |
| end
 | |
| 
 | |
| local function getTapeDrive(side)
 | |
|     local tape
 | |
|     if side then
 | |
|         tape = peripheral.wrap(side)
 | |
|         if not tape then
 | |
|             return false, "No such tape drive found.", 0
 | |
|         elseif peripheral.getType(tape) ~= "tape_drive" then
 | |
|             return false, "Not a tape drive.", 0
 | |
|         else
 | |
|             return true, tape, tape.getSize()
 | |
|         end
 | |
|     else
 | |
|         tape = peripheral.find("tape_drive")
 | |
|         if not tape then
 | |
|             return false, "Tape drive not connected.", 0
 | |
|         else
 | |
|             return true, tape, tape.getSize()
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| local function writeToTape(tape, contents, tapeName)
 | |
|     local output = ""
 | |
|     if not tape then error("expected tape peripheral") end
 | |
|     if not contents then error("expected contents") end
 | |
| 
 | |
|     local totalTapeSize = tape.getSize()
 | |
| 
 | |
|     if tapeName then
 | |
|         tape.setLabel(tapeName)
 | |
|     end
 | |
| 
 | |
|     tape.stop()
 | |
|     tape.seek(-tape.getPosition())
 | |
|     if #contents > totalTapeSize then
 | |
|         output = "Tape too small, audio truncated."
 | |
|         for i = 0, 8 do
 | |
|             if tierInfo[i] then
 | |
|                 if tierInfo[i] * 360000 >= #contents then
 | |
|                     output = output .. "\nIt would fit on a" .. (tierInfo[i] == 8 and "n " or " ") .. tostring(tierInfo[i]) .. " minute tape."
 | |
|                     break
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|         contents = contents:sub(1, totalTapeSize)
 | |
|     end
 | |
|     tape.write(contents)
 | |
|     tape.seek(-tape.getPosition())
 | |
|     tape.stop()
 | |
| 
 | |
|     return output
 | |
| end
 | |
| 
 | |
| local function infoMode(tape)
 | |
|     local scr_x, scr_y = term.getSize()
 | |
|     print("Info for tape drive:")
 | |
|     print(("-"):rep(scr_x))
 | |
| 
 | |
|     local gi = tape.getItem(1).getMetadata()
 | |
| 
 | |
|     local info = {
 | |
|         inserted = gi ~= nil
 | |
|     }
 | |
| 
 | |
|     local minute = 360000
 | |
| 
 | |
|     if not info.inserted then
 | |
|         print("No tape inserted.")
 | |
|     else
 | |
|         info.displayname = gi.displayName
 | |
|         info.label = gi.media.label
 | |
|         info.itemname = gi.name
 | |
|         info.damage = gi.damage
 | |
|         info.minutes = tierInfo[info.damage]
 | |
|         info.maxsize = tape.getSize()
 | |
|         info.position = tape.getPosition()
 | |
| 
 | |
|         print(info.displayname .. " inserted.")
 | |
|         print("Label: \"" .. info.label .. "\"")
 | |
|         write("Stores " .. tostring(info.minutes) .. " minutes")
 | |
|         print(" (" .. tostring(info.maxsize) .. " bytes)")
 | |
|         print("Seeked to " .. tostring(info.position) .. "/" .. tostring(info.maxsize))
 | |
|     end
 | |
|     return info
 | |
| end
 | |
| 
 | |
| local argInfo = {
 | |
|     [1] = {0, "h", "help"},
 | |
|     [2] = {0, "i", "info"},
 | |
|     [3] = {1, "d", "drive"},
 | |
|     [4] = {0, "e", "erase"},
 | |
|     [5] = {0, "u", "unclean"},
 | |
|     [6] = {1, "c", "cc-media"},
 | |
|     [7] = {0, "r", "reinstall"}
 | |
| }
 | |
| 
 | |
| local arguments, options, errors = argParse({...}, argInfo)
 | |
| 
 | |
| local tape, totalTapeSize, success, contents
 | |
| 
 | |
| if #errors > 0 then
 | |
|     for i = 1, #errors do
 | |
|         printError(errors[i])
 | |
|     end
 | |
|     return
 | |
| end
 | |
| 
 | |
| local fileName = arguments[1]
 | |
| local tapeName = arguments[2]
 | |
| local tapeDriveName = options[3][1]
 | |
| local wantedHelp = options[1] == true
 | |
| local wantedInfo = options[2] == true
 | |
| local wantedErase = options[4] == true
 | |
| local doUnclean = options[5] == true
 | |
| local getFromGithub = options[6][1]
 | |
| local wantedUpdate = options[7] == true
 | |
| 
 | |
| if wantedHelp then
 | |
|     if wantedInfo then
 | |
|         getHelp("--info")
 | |
|     elseif wantedErase then
 | |
|         getHelp("--erase")
 | |
|     elseif doUnclean then
 | |
|         getHelp("--unclean")
 | |
|     elseif getFromGithub then
 | |
|         getHelp("--ldd-github")
 | |
|     elseif wantedUpdate then
 | |
|         getHelp("--reinstall")
 | |
|     else
 | |
|         getHelp()
 | |
|     end
 | |
|     return
 | |
| end
 | |
| 
 | |
| if wantedUpdate then
 | |
|     print("Redownloading Mtape...")
 | |
|     local cpath = shell.getRunningProgram()
 | |
|     local file = http.get("https://github.com/LDDestroier/CC/raw/master/mtape.lua")
 | |
|     local contents
 | |
| 
 | |
|     local sizeBefore = fs.getSize(cpath)
 | |
|     local sizeAfter = 0
 | |
|     
 | |
|     if file then
 | |
|         contents = file.readAll()
 | |
|         sizeAfter = #contents
 | |
|         file.close()
 | |
|         file = fs.open(cpath, "w")
 | |
|         file.write(contents)
 | |
|         file.close()
 | |
|         print("Done! (diff: " .. tostring(sizeAfter - sizeBefore) .. " bytes)")
 | |
|         return
 | |
|     else
 | |
|         print("Couldn't update.")
 | |
|         return
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- Try to wrap specified tape drive OR find tape drive
 | |
| success, tape, totalTapeSize = getTapeDrive(tapeDriveName)
 | |
| if not success then
 | |
|     print(tape)
 | |
|     return
 | |
| end
 | |
| 
 | |
| if wantedInfo then
 | |
|     infoMode(tape)
 | |
|     return true
 | |
| end
 | |
| 
 | |
| if wantedErase then
 | |
|     success = writeToTape(tape, string.char(0):rep(tape.getSize()), tapeName)
 | |
|     print("Tape erased.")
 | |
|     return
 | |
| end
 | |
| 
 | |
| if getFromGithub then
 | |
|     print("Pulling from CC-Media.")
 | |
|     tapeName = arguments[1]
 | |
|     fileName = "https://github.com/LDDestroier/CC-Media/raw/master/DFPWM/" .. getFromGithub .. ".dfpwm"
 | |
| end
 | |
| 
 | |
| if not fileName then
 | |
|     getHelp()
 | |
|     return
 | |
| end
 | |
| 
 | |
| -- Make file, and detect URL
 | |
| if fileName:sub(1,8) == "https://" then
 | |
|     write("Downloading...")
 | |
|     success, contents = getFileContents(fileName, true)
 | |
|     print("Done.")
 | |
| else
 | |
|     if not fs.exists(fileName) then
 | |
|         fileName = fileName .. ".dfpwm"
 | |
|     end
 | |
|     success, contents = getFileContents(fileName, false)
 | |
| end
 | |
| 
 | |
| if not success then
 | |
|     error("Could not get file. Abort.")
 | |
| else
 | |
|     if _DEBUG then
 | |
|         print("File size: " .. tostring(#contents) .. " bytes")
 | |
|         print("Tape size: " .. tostring(tape.getSize()) .. " bytes")
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- Adds null data to the end of the file if you want a clean tape write
 | |
| if doUnclean then
 | |
|     print("Won't clean up tape first.")
 | |
| else
 | |
|     contents = contents .. string.char(0):rep(math.max(0, totalTapeSize - #contents))
 | |
| end
 | |
| 
 | |
| write("Writing...")
 | |
| 
 | |
| success = writeToTape(tape, contents, tapeName)
 | |
| if success then
 | |
|     print("\n" .. success)
 | |
|     print("Nonetheless, it is written.")
 | |
| else
 | |
|     print("done!")
 | |
| end
 | |
| 
 | |
| return true
 | 
