mirror of
				https://github.com/LDDestroier/CC/
				synced 2025-10-31 07:22:59 +00:00 
			
		
		
		
	Create nbs5.lua
This commit is contained in:
		
							
								
								
									
										303
									
								
								nbs5.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								nbs5.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,303 @@ | ||||
| --[[ | ||||
| NoteBlock Song API | ||||
| by MysticT | ||||
| Twekaed by Soni | ||||
| Modified for NBS v5 by LDDestroier (WIP) | ||||
| --]] | ||||
|  | ||||
| local nbs5 = {} | ||||
|  | ||||
| -- yield to avoid error | ||||
| local function yield() | ||||
| 	os.queueEvent("yield") | ||||
| 	os.pullEvent("yield") | ||||
| end | ||||
|  | ||||
| -- read short integer (16-bit) from file | ||||
| local function readShort(file) | ||||
| 	return file.read() + file.read() * 256 | ||||
| end | ||||
| _G.readShort = readShort | ||||
|  | ||||
|  | ||||
| -- read integer (32-bit) from file | ||||
| local function readInt(file) | ||||
| 	return file.read() + file.read() * 256 + file.read() * 65536 + file.read() * 16777216 | ||||
| end | ||||
|  | ||||
| -- read string from file | ||||
| local function readString(file) | ||||
| 	local s, c = "" | ||||
| 	local len = readInt(file) | ||||
| 	for i = 1, len do | ||||
| 		c = file.read() | ||||
| 		if not c then | ||||
| 			break | ||||
| 		end | ||||
| 		s = s .. string.char(c) | ||||
| 	end | ||||
| 	return s | ||||
| end | ||||
|  | ||||
| local function makeBitNum(num, bytes) | ||||
| 	local output = "" | ||||
| 	for i = 0, bytes - 1 do | ||||
| 		output = output .. string.char(bit32.band(bit32.rshift(num, i * 8), 0xFF)) | ||||
| 	end | ||||
| 	return output | ||||
| end | ||||
|  | ||||
| -- write short integer (16-bit) to file | ||||
| local function writeShort(file, int) | ||||
| 	file.write(makeBitNum(int, 2)) | ||||
| end | ||||
|  | ||||
| -- write integer (32-bit) to file | ||||
| local function writeInt(file, int) | ||||
| 	file.write(makeBitNum(int, 4)) | ||||
| end | ||||
|  | ||||
| -- write string to file | ||||
| local function writeString(file, str) | ||||
| 	writeInt(file, #str) | ||||
| 	file.write(str) | ||||
| end | ||||
|  | ||||
| -- read nbs file header | ||||
| local function readNBSHeader(file) | ||||
| 	local header = {} | ||||
| 	header.lenght = readShort(file) | ||||
| 	if header.lenght > 0 then | ||||
| 		-- old NBS | ||||
| 		header.nbs_version = 0 | ||||
| 		header.height = readShort(file) | ||||
| 		header.name = readString(file) | ||||
| 		if header.name == "" then | ||||
| 			header.name = "Untitled" | ||||
| 		end | ||||
| 		header.author = readString(file) | ||||
| 		if header.author == "" then | ||||
| 			header.author = "Unknown" | ||||
| 		end | ||||
| 		header.original_author = readString(file) | ||||
| 		if header.original_author == "" then | ||||
| 			header.original_author = "Unknown" | ||||
| 		end | ||||
| 		header.description = readString(file) | ||||
| 		header.tempo = readShort(file) / 100 | ||||
| 		header.autosave = file.read() | ||||
| 		header.autosave_duration = file.read() | ||||
| 		header.time_signature = file.read() | ||||
| 		header.minutes_spent = readInt(file) | ||||
| 		header.left_clicks = readInt(file) | ||||
| 		header.right_clicks = readInt(file) | ||||
| 		header.blocks_added = readInt(file) | ||||
| 		header.blocks_removed = readInt(file) | ||||
| 		header.filename = readString(file) | ||||
| 		 | ||||
| 	else | ||||
| 		-- NBS v5 | ||||
| 		header.nbs_version = file.read() | ||||
| 		header.vanilla_instruments = file.read() | ||||
| 		header.lenght = readShort(file) | ||||
| 		header.height = readShort(file) | ||||
| 		header.name = readString(file) | ||||
| 		if header.name == "" then | ||||
| 			header.name = "Untitled" | ||||
| 		end | ||||
| 		header.author = readString(file) | ||||
| 		if header.author == "" then | ||||
| 			header.author = "Unknown" | ||||
| 		end | ||||
| 		header.original_author = readString(file) | ||||
| 		if header.original_author == "" then | ||||
| 			header.original_author = "Unknown" | ||||
| 		end | ||||
| 		header.description = readString(file) | ||||
| 		header.tempo = readShort(file) / 100 | ||||
| 		header.autosave = file.read() | ||||
| 		header.autosave_duration = file.read() | ||||
| 		header.time_signature = file.read() | ||||
| 		header.minutes_spent = readInt(file) | ||||
| 		header.left_clicks = readInt(file) | ||||
| 		header.right_clicks = readInt(file) | ||||
| 		header.blocks_added = readInt(file) | ||||
| 		header.blocks_removed = readInt(file) | ||||
| 		header.filename = readString(file) | ||||
| 		header.looping = file.read() | ||||
| 		header.max_looping = file.read() | ||||
| 		header.loop_start = readShort(file) | ||||
| 		 | ||||
| 	end | ||||
|  | ||||
| 	return header | ||||
| end | ||||
|  | ||||
| -- jump to the next tick in the file | ||||
| local function nextTick(file, tSong) | ||||
| 	local jump = readShort(file) | ||||
| 	for i = 1, jump - 1 do | ||||
| 		tSong[#tSong + 1] = {} | ||||
| 	end | ||||
| 	return jump > 0 | ||||
| end | ||||
|  | ||||
| -- read the notes in a tick | ||||
| -- TODO cleanup, move checks to player:nbs.lua | ||||
| local function readTick(file) | ||||
| 	local t = {} | ||||
| 	local n = 0 | ||||
| 	local jump = readShort(file) | ||||
|  | ||||
| 	while jump > 0 do | ||||
| 		n = n + jump | ||||
| 		write(".") | ||||
| 		local instrument = file.read() + 1 | ||||
| 		if instrument > 16 then | ||||
| 			return nil, "(v5) Can't convert custom instruments" | ||||
| 		end | ||||
|  | ||||
| 		local note = file.read() - 33 | ||||
| 		local velocity = file.read() | ||||
| 		local pan = file.read() | ||||
| 		local pitch_mod = readShort(file) | ||||
|  | ||||
| 		if note < 0 or note > 24 then | ||||
| 			return nil, "(v5) Notes must be in Minecraft's 2 octaves (" .. note .. ")" | ||||
| 		end | ||||
|  | ||||
| 		if not t[instrument] then | ||||
| 			t[instrument] = {} | ||||
| 		end | ||||
|  | ||||
| 		t[instrument][n] = note | ||||
| 		jump = readShort(file) | ||||
| 	end | ||||
|  | ||||
| 	return t | ||||
| end | ||||
|  | ||||
| -- API functions | ||||
|  | ||||
| -- save a converted song to a file | ||||
| function nbs5.saveSong(tSong, sPath) | ||||
| 	local file = fs.open(sPath, "w") | ||||
| 	if file then | ||||
| 		file.write(textutils.serialize(tSong)) | ||||
| 		file.close() | ||||
| 		return true | ||||
| 	end | ||||
|  | ||||
| 	return false, "Error opening file "..sPath | ||||
| end | ||||
|  | ||||
| -- save a song as an NBS v5 file | ||||
| function nbs5.saveSongNBS(tSong, sPath) | ||||
| 	 | ||||
| end | ||||
|  | ||||
| -- load and convert an .nbs file and save it | ||||
| function nbs5.load(sPath, bVerbose) | ||||
| 	local file = fs.open(sPath, "rb") | ||||
| 	if file then | ||||
| 		if bVerbose then | ||||
| 			print("Reading header...") | ||||
| 		end | ||||
|  | ||||
| 		local tSong = {} | ||||
| 		local header = readNBSHeader(file) | ||||
| 		tSong.header = header | ||||
| 		tSong.name = header.name | ||||
| 		tSong.author = header.author | ||||
| 		tSong.original_author = header.original_author | ||||
| 		tSong.lenght = header.lenght / header.tempo | ||||
| 		tSong.delay = 1 / header.tempo | ||||
|  | ||||
| 		if bVerbose then | ||||
| 			print("Reading ticks...") | ||||
| 		end | ||||
|  | ||||
| 		if header.nbs_version == 0 then | ||||
|  | ||||
| 			while nextTick(file, tSong) do | ||||
| 				local tick, err = readTick(file, tSong) | ||||
| 				if tick then | ||||
| 					table.insert(tSong, tick) | ||||
| 				else | ||||
| 					file.close() | ||||
| 					return nil, err | ||||
| 				end | ||||
| 				yield() | ||||
| 			end | ||||
|  | ||||
| 			pcall(function() | ||||
| 				local layers = {} | ||||
| 				for i=1, header.height do | ||||
| 					table.insert(layers, { | ||||
| 						name=readString(file), | ||||
| 						volume=file.read() + 0 | ||||
| 					}) | ||||
| 				end | ||||
| 				tSong.layers = layers | ||||
|  | ||||
| 				local insts = {} | ||||
| 				for i=1, file.read() + 0 do | ||||
| 					insts[#insts + 1] = { | ||||
| 						name = readString(file), | ||||
| 						file = readString(file), | ||||
| 						pitch = file.read() + 0, | ||||
| 						key = (file.read() + 0) ~= 0 | ||||
| 					} | ||||
| 				end | ||||
| 				tSong.instruments = insts | ||||
| 			end) | ||||
| 		 | ||||
| 		else | ||||
|  | ||||
| 			while nextTick(file, tSong) do | ||||
| 				local tick, err = readTick(file, tSong) | ||||
| 				if tick then | ||||
| 					table.insert(tSong, tick) | ||||
| 				else | ||||
| 					file.close() | ||||
| 					return nil, err | ||||
| 				end | ||||
| 				yield() | ||||
| 			end | ||||
|  | ||||
| 			pcall(function() | ||||
| 				local layers = {} | ||||
| 				for i=1, header.height do | ||||
| 					table.insert(layers, { | ||||
| 						name = readString(file), | ||||
| 						lock = file.read(), | ||||
| 						volume = file.read(), | ||||
| 						pan = file.read() | ||||
| 					}) | ||||
| 				end | ||||
| 				tSong.layers = layers | ||||
|  | ||||
| 				local insts = {} | ||||
| 				for i=1, file.read() + 0 do | ||||
| 					insts[#insts + 1] = { | ||||
| 						name = readString(file), | ||||
| 						file = readString(file), | ||||
| 						pitch = file.read() + 0, | ||||
| 						key = (file.read() + 0) ~= 0 | ||||
| 					} | ||||
| 				end | ||||
| 				tSong.instruments = insts | ||||
| 			end) | ||||
|  | ||||
| 		end | ||||
|  | ||||
| 		file.close() | ||||
|  | ||||
| 		return tSong | ||||
| 	end | ||||
|  | ||||
| 	return nil, "Error opening file "..sPath | ||||
|  | ||||
| end | ||||
|  | ||||
| return nbs5 | ||||
		Reference in New Issue
	
	Block a user
	 LDDestroier
					LDDestroier