nbs-reader
Version:
This package is a library that processes Note Block Studio nbs files in Note.js.
242 lines (238 loc) • 8.05 kB
JavaScript
var fs = require("fs");
function decodeText(octets) {
var string = "";
var i = 0;
while (i < octets.length) {
var octet = octets[i];
var bytesNeeded = 0;
var codePoint = 0;
if (octet <= 0x7F) {
bytesNeeded = 0;
codePoint = octet & 0xFF;
} else if (octet <= 0xDF) {
bytesNeeded = 1;
codePoint = octet & 0x1F;
} else if (octet <= 0xEF) {
bytesNeeded = 2;
codePoint = octet & 0x0F;
} else if (octet <= 0xF4) {
bytesNeeded = 3;
codePoint = octet & 0x07;
}
if (octets.length - i - bytesNeeded > 0) {
var k = 0;
while (k < bytesNeeded) {
octet = octets[i + k + 1];
codePoint = (codePoint << 6) | (octet & 0x3F);
k += 1;
}
} else {
codePoint = 0xFFFD;
bytesNeeded = octets.length - i;
}
string += String.fromCodePoint(codePoint);
i += bytesNeeded + 1;
}
return string
};
module.exports = ((path) => {
var pos = 0;
var _;
var Get = (Data, Byte, Little, Converter) => {
var out = Data.substring(pos, pos + (Byte * 2));
if (Little)
out = out.match(/.{2}/g).reverse().join("");
pos += Byte * 2;
if (!Converter)
return out;
else
return Converter(out);
}
var Int = (Data) => parseInt(Data, 16);
var Chars = (Data) => {
if (Data.length == 0)
return "";
return decodeText(new Uint8Array(Data.match(/.{2}/g).map(x => parseInt(x, 16))));
}
var GetNbsChar = (Data) => {
var len = Get(Data, 4, true, Int);
return Get(Data, len, false, Chars);
};
var ReadByte = (Data) => Get(Data, 1, true, Int);
var data = fs.readFileSync(path)
var hex = data.toString("hex").toUpperCase();
var length = Get(hex, 2, true, Int);
function opennbs() {
var nbs = {
OpenNbs: true,
Version: ReadByte(hex),
VanillaInstruments: ReadByte(hex),
Length: Get(hex, 2, true, Int),
Layers: Get(hex, 2, true, Int),
SongName: GetNbsChar(hex),
SongAuthor: GetNbsChar(hex),
OriginalAuthor: GetNbsChar(hex),
Description: GetNbsChar(hex),
Tempo: Get(hex, 2, true, Int) / 100,
AutoSave: {
Enable: new Boolean(ReadByte(hex)),
Duration: ReadByte(hex)
},
TimeSignature: ReadByte(hex),
MinutesSpent: Get(hex, 4, true, Int),
LeftClicks: Get(hex, 4, true, Int),
RightClicks: Get(hex, 4, true, Int),
BlocksAdded: Get(hex, 4, true, Int),
BlocksRemoved: Get(hex, 4, true, Int),
FileName: GetNbsChar(hex),
Loop: {
Enable: new Boolean(ReadByte(hex)),
Count: ReadByte(hex),
StartTick: Get(hex, 2, true, Int)
},
Notes: [],
CustomInstruments: []
};
var notes = [];
var tick = -1;
var layernotes = {};
while (true) {
var jumps = Get(hex, 2, true, Int);
if (jumps == 0) {
break;
} else if (jumps > 1) {
for (var i = 0; i < jumps - 1; i++) {
notes[tick + i + 1] = [];
}
}
tick += jumps;
var layer = -1;
while (true) {
jumps = Get(hex, 2, true, Int);
if (jumps == 0) {
break;
}
layer += jumps;
var note = {
Tick: tick,
Layer: layer,
Inst: ReadByte(hex),
Key: ReadByte(hex),
Velocity: ReadByte(hex),
Panning: ReadByte(hex),
Pitch: Get(hex, 2, true, Int)
};
if (notes[tick])
notes[tick].push(note);
else
notes[tick] = [note];
if (layer < nbs.Layers)
if (layernotes[layer])
layernotes[layer][tick] = note;
else {
layernotes[layer] = {};
layernotes[layer][tick] = note;
}
}
}
var layers = nbs.Layers;
nbs.Layers = [];
for (var i = 0; i < layers; i++) {
var layer = { Name: GetNbsChar(hex), Locked: new Boolean(ReadByte(hex)), Volume: ReadByte(hex), Panning: ReadByte(hex), Notes: layernotes[i] };
nbs.Layers.push(layer);
}
nbs.Notes = notes;
var len = ReadByte(hex);
for (var i = 0; i < len; i++) {
nbs.CustomInstruments.push({ Name: GetNbsChar(hex), FileName: GetNbsChar(hex), Pitch: ReadByte(hex), PressKey: new Boolean(ReadByte(hex)) });
}
return nbs;
}
if (length == 0)
return opennbs();
var nbs = {
OpenNbs: false,
Version: 0,
VanillaInstruments: 10,
Length: length + 1,
Layers: Get(hex, 2, true, Int),
SongName: GetNbsChar(hex),
SongAuthor: GetNbsChar(hex),
OriginalAuthor: GetNbsChar(hex),
Description: GetNbsChar(hex),
Tempo: Get(hex, 2, true, Int) / 100,
AutoSave: {
Enable: new Boolean(ReadByte(hex)),
Duration: ReadByte(hex)
},
TimeSignature: ReadByte(hex),
MinutesSpent: Get(hex, 4, true, Int),
LeftClicks: Get(hex, 4, true, Int),
RightClicks: Get(hex, 4, true, Int),
BlocksAdded: Get(hex, 4, true, Int),
BlocksRemoved: Get(hex, 4, true, Int),
FileName: GetNbsChar(hex),
Loop: {
Enable: false,
Count: 0,
StartTick: 0
},
Notes: [],
CustomInstruments: []
}
var notes = [];
var tick = -1;
var jumps = 0;
var layernotes = {};
while (true) {
jumps = Get(hex, 2, true, Int);
if (jumps == 0) {
break;
} else if (jumps > 1) {
for (var i = 0; i < jumps - 1; i++) {
notes[tick + i + 1] = [];
}
}
tick += jumps;
var layer = -1;
while (true) {
jumps = Get(hex, 2, true, Int);
if (jumps == 0) {
break;
}
layer += jumps;
var note = {
Tick: tick,
Layer: layer,
Inst: ReadByte(hex),
Key: ReadByte(hex),
Velocity: 100,
Panning: 100,
Pitch: 0
};
if (notes[tick])
notes[tick].push(note);
else
notes[tick] = [note];
if (layer < nbs.Layers)
if (layernotes[layer])
layernotes[layer][tick] = note;
else {
layernotes[layer] = {};
layernotes[layer][tick] = note;
}
}
}
var layers = nbs.Layers;
nbs.Layers = [];
for (var i = 0; i < layers; i++) {
var layer = { Name: GetNbsChar(hex), Volume: ReadByte(hex), Notes: layernotes[i] };
nbs.Layers.push(layer);
}
nbs.Notes = notes;
var len = ReadByte(hex);
for (var i = 0; i < len; i++) {
nbs.CustomInstruments.push({ Name: GetNbsChar(hex), FileName: GetNbsChar(hex), Pitch: ReadByte(hex), PressKey: new Boolean(ReadByte(hex)) });
}
return nbs;
});