diff --git a/util/nbs-file.js b/util/nbs-file.js new file mode 100644 index 0000000..939a7df --- /dev/null +++ b/util/nbs-file.js @@ -0,0 +1,157 @@ +function parse (buffer) { + let i = 0 + + let songLength = 0 + let format = 0 + let vanillaInstrumentCount = 0 + songLength = readShort() + if (songLength === 0) { + format = readByte() + } + + if (format >= 1) { + vanillaInstrumentCount = readByte() + } + if (format >= 3) { + songLength = readShort() + } + + const layerCount = readShort() + const songName = readString() + const songAuthor = readString() + const songOriginalAuthor = readString() + const songDescription = readString() + const tempo = readShort() + const autoSaving = readByte() + const autoSavingDuration = readByte() + const timeSignature = readByte() + const minutesSpent = readInt() + const leftClicks = readInt() + const rightClicks = readInt() + const blocksAdded = readInt() + const blocksRemoved = readInt() + const origFileName = readString() + + let loop = 0 + let maxLoopCount = 0 + let loopStartTick = 0 + if (format >= 4) { + loop = readByte() + maxLoopCount = readByte() + loopStartTick = readShort() + } + + const nbsNotes = [] + let tick = -1 + while (true) { + const tickJumps = readShort() + if (tickJumps === 0) break + tick += tickJumps + + let layer = -1 + while (true) { + const layerJumps = readShort() + if (layerJumps === 0) break + layer += layerJumps + const note = nbsNote() + note.tick = tick + note.layer = layer + note.instrument = readByte() + note.key = readByte() + if (format >= 4) { + note.velocity = readByte() + note.panning = readByte() + note.pitch = readShort() + } + nbsNotes.push(note) + } + } + + const nbsLayers = [] + if (i <= buffer.length) { + for (let j = 0; j < layerCount; j++) { + const layer = nbsLayer() + layer.name = readString() + if (format >= 4) { + layer.lock = readByte() + } + layer.volume = readByte() + if (format >= 2) { + layer.stereo = readByte() + } + nbsLayers.push(layer) + } + } + + return { + songLength, + format, + vanillaInstrumentCount, + layerCount, + songName, + songAuthor, + songOriginalAuthor, + songDescription, + tempo, + autoSaving, + autoSavingDuration, + timeSignature, + minutesSpent, + leftClicks, + rightClicks, + blocksAdded, + blocksRemoved, + origFileName, + loop, + maxLoopCount, + loopStartTick, + nbsNotes, + nbsLayers + } + + function readByte () { + return buffer.readInt8(i++) + } + + function readShort () { + const short = buffer.readInt16LE(i) + i += 2 + return short + } + + function readInt () { + const int = buffer.readInt32LE(i) + i += 4 + return int + } + + function readString () { + let length = readInt() + let string = '' + for (; i > 0; i--) string += String.fromCharCode(readByte()) + return string + } +} + +function nbsNote () { + return { + tick: null, + layer: null, + instrument: null, + key: null, + velocity: 100, + panning: 100, + pitch: 0 + } +} + +function nbsLayer () { + return { + name: null, + lock: 0, + volume: null, + stereo: 100 + } +} + +module.exports = { parse, nbsNote, nbsLayer } diff --git a/util/regexp.js b/util/regexp.js new file mode 100644 index 0000000..f2d5bd5 --- /dev/null +++ b/util/regexp.js @@ -0,0 +1,7 @@ +// Useful RegExp s +module.exports = { + ESCAPE_SEQUENCE: /\xa7.?/g, + VALID_ESCAPE_SEQUENCE: /\xa7[0-9a-fkl-orA-FKL-OR]/g, + INVALID_ESCAPE_SEQUENCE: /\xa7[^0-9a-fkl-orA-FKL-OR]/g, + UUID: /^[0-9a-fA-F]{8}\b\-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/ +} diff --git a/util/stylize_with_color.js b/util/stylize_with_color.js new file mode 100644 index 0000000..a192722 --- /dev/null +++ b/util/stylize_with_color.js @@ -0,0 +1,23 @@ +const styles = { + special: '\xa73', + number: '\xa76', + bigint: '\xa76', + boolean: '\xa76', + undefined: '\xa78', + null: '\xa7l', + string: '\xa72', + symbol: '\xa72', + date: '\xa75', + // "name": intentionally not styling + // TODO(BridgeAR): Highlight regular expressions properly. + regexp: '\xa74', + module: '\xa7n' +} + +function stylizeWithColor (str, styleType) { + const style = styles[styleType] + if (style !== undefined) return `${style}${str}\xa7r` + return str +} + +module.exports = { stylizeWithColor, styles } diff --git a/util/uuid-to-nbt-uuid.js b/util/uuid-to-nbt-uuid.js new file mode 100644 index 0000000..a3c0401 --- /dev/null +++ b/util/uuid-to-nbt-uuid.js @@ -0,0 +1,8 @@ +// originally from https://gist.github.com/storycraft/7813dd0186e85daa393e1df9cfa19f2a +const nbt = require('prismarine-nbt') + +function toNBTUUID (uuid) { + return nbt.intArray(nbt.int(uuid.replace(/-/g, '').match(/.{8}/g).map(str => Number.parseInt(str, 16)).map(num => num & 0x80000000 ? num - 0xffffffff - 1 : num))) +} + +module.exports = toNBTUUID