Barnet

OnlyNBT

Jun 10th, 2021 (edited)
122
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --[[
  2.   NoteBlock Song API
  3.   by MysticT
  4.   Twekaed by Soni
  5. --]]
  6.  
  7. -- yield to avoid error
  8. local function yield()
  9.   os.queueEvent("fake")
  10.   os.pullEvent("fake")
  11. end
  12.  
  13. -- read short integer (16-bit) from file
  14. local function readShort(file)
  15.   return file.read() + file.read() * 256
  16. end
  17.  
  18. -- read integer (32-bit) from file
  19. local function readInt(file)
  20.   return file.read() + file.read() * 256 + file.read() * 65536 + file.read() * 16777216
  21. end
  22.  
  23. -- read string from file
  24. local function readString(file)
  25.   local s = ""
  26.   local len = readInt(file)
  27.   for i = 1, len do
  28.     local c = file.read()
  29.     if not c then
  30.       break
  31.     end
  32.     s = s..string.char(c)
  33.   end
  34.   return s
  35. end
  36.  
  37. -- read nbs file header
  38. local function readNBSHeader(file)
  39.   local header = {}
  40.   header.lenght = readShort(file)
  41.   header.height = readShort(file)
  42.   header.name = readString(file)
  43.   if header.name == "" then
  44.     header.name = "Untitled"
  45.   end
  46.   header.author = readString(file)
  47.   if header.author == "" then
  48.     header.author = "Unknown"
  49.   end
  50.   header.original_author = readString(file)
  51.   if header.original_author == "" then
  52.     header.original_author = "Unknown"
  53.   end
  54.   header.description = readString(file)
  55.   header.tempo = readShort(file) / 100
  56.   header.autosave = file.read()
  57.   header.autosave_duration = file.read()
  58.   header.time_signature = file.read()
  59.   header.minutes_spent = readInt(file)
  60.   header.left_clicks = readInt(file)
  61.   header.right_clicks = readInt(file)
  62.   header.blocks_added = readInt(file)
  63.   header.blocks_removed = readInt(file)
  64.   header.filename = readString(file)
  65.   return header
  66. end
  67.  
  68. -- jump to the next tick in the file
  69. local function nextTick(file, tSong)
  70.   local jump = readShort(file)
  71.   for i = 1, jump - 1 do
  72.     table.insert(tSong, {})
  73.   end
  74.   return jump > 0
  75. end
  76.  
  77. -- read the notes in a tick
  78. -- TODO cleanup, move checks to player:nbs.lua
  79. local function readTick(file)
  80.   local t = {}
  81.   local n = 0
  82.   local jump = readShort(file)
  83.   while jump > 0 do
  84.     n = n + jump
  85.     local instrument = file.read() + 1
  86.     if instrument > 7 then -- Soni: we actually support 7 instruments
  87.       return nil, "Can't convert custom instruments"
  88.     end
  89.     local note = file.read() - 33
  90.     if note < 0 or note > 24 then
  91.       return nil, "Notes must be in Minecraft's 2 octaves"
  92.     end
  93.     if not t[instrument] then
  94.       t[instrument] = {}
  95.     end
  96.     t[instrument][n] = note
  97.     jump = readShort(file)
  98.   end
  99.   return t
  100. end
  101.  
  102. -- API functions
  103.  
  104. -- save a converted song to a file
  105. function saveSong(tSong, sPath)
  106.   local file = fs.open(sPath, "w")
  107.   if file then
  108.     file.write(textutils.serialize(tSong))
  109.     file.close()
  110.     return true
  111.   end
  112.   return false, "Error opening file "..sPath
  113. end
  114.  
  115. -- load and convert an .nbs file and save it
  116. function load(sPath, bVerbose)
  117.   local file = fs.open(sPath, "rb")
  118.   if file then
  119.     if bVerbose then
  120.       print("Reading header...")
  121.     end
  122.     local tSong = {}
  123.     local header = readNBSHeader(file)
  124.     tSong.name = header.name
  125.     tSong.author = header.author
  126.     tSong.original_author = header.original_author
  127.     tSong.lenght = header.lenght / header.tempo
  128.     tSong.delay = 1 / header.tempo
  129.     if bVerbose then
  130.       print("Reading ticks...")
  131.     end
  132.     while nextTick(file, tSong) do
  133.       local tick, err = readTick(file, tSong)
  134.       if tick then
  135.         table.insert(tSong, tick)
  136.       else
  137.         file.close()
  138.         return nil, err
  139.       end
  140.       yield()
  141.     end
  142.  
  143.     pcall(function()
  144.         local layers = {}
  145.         for i=1, header.height do
  146.           table.insert(layers, {name=readString(file), volume=file.read() + 0})
  147.         end
  148.         tSong.layers = layers
  149.        
  150.         local insts = {}
  151.         for i=1, file.read() + 0 do
  152.           table.insert(insts, {name=readString(file), file=readString(file), pitch=file.read() + 0, key=(file.read() + 0) ~= 0})
  153.         end
  154.         tSong.instruments = insts
  155.       end)
  156.     file.close()
  157.     return tSong
  158.   end
  159.   return nil, "Error opening file "..sPath
  160. end
  161.  
  162. return {yield = yield, readShort = readShort, readInt = readInt, readString = readString, readNBSHeader = readNBSHeader, nextTick = nextTick, readTick = readTick, saveSong = saveSong, load = load}
RAW Paste Data