Skip to content

Instantly share code, notes, and snippets.

@leppie
Created November 4, 2025 15:44
Show Gist options
  • Save leppie/90d0bc2c4633d38936dcd498d500b38d to your computer and use it in GitHub Desktop.
Save leppie/90d0bc2c4633d38936dcd498d500b38d to your computer and use it in GitHub Desktop.
Use in VSCode with Binary File Viewer extension (also C# stuff)
registerFileType((fileExt, filePath, fileData) => {
if (fileExt == 'ncs') {
const headerArray = fileData.getBytesAt(0, 4);
const header = String.fromCharCode(...headerArray)
if (header === 'USER') {
return true;
}
}
return false;
});
/*
TODO:
* Project FX
* Sidechain
* Default macros for presets
* Automation expansion
*/
registerParser(() => {
setDefaults({
"colors": {
"collapse": "blue",
"offset": "yellow",
"size": "yellow",
"name": "white",
"value": "magenta",
"description": "green",
"row-header": "cyan",
"row-odd": "lightgray",
"row-even": "lightgray",
},
"dark-colors": {
"collapse": "blue",
"offset": "red",
"size": "green",
"name": "magenta",
"value": "black",
"description": "magenta",
"row-header": "yellow",
"row-odd": "lightgray",
"row-even": "lightgray",
}
});
addStandardHeader();
read(4);
addRow('Header', getStringValue(), "Always USER");
read(4);
addRow('Filesize', getDecimalValue(), "Always 160780");
read(4);
addRow('Unknown1', getHex0xValue(), "Always 1? File version?");
read(4);
addRow('RootNote', getDecimalValue(), "Weird behavior");
read(32);
addRow('Name', getStringValue(), "Padded with spaces");
read(4);
addRow('Unknown2', getHex0xValue(), "Always zeros");
read(1);
addRow('Tempo', getDecimalValue());
read(1);
addRow('Swing', getDecimalValue());
read(1);
addRow('Unknown3b', getHex0xValue(), "Always 3?");
read(1);
addRow('Unknown3c', getHex0xValue(), "Always zeros");
read(8);
addRow("Unknown8a", getHex0xValue(), "Always zeros");
read(644);
addRow('Scene Manager');
addDetails(() => {
for (var s = 0; s < 16; s++) {
read(40);
addRow("Scene " + (s + 1));
addDetails(()=> {
addPatternMaps();
read(1);
addRow("Enabled", getDecimalValue());
read(1);
addRow("Muted", getBitsValue());
read(6);
addRow("??", getHex0xValue(), "Always zeros?");
});
}
read(4);
addRow("Settings", getHex0xValue());
addDetails(()=> {
read(1);
addRow("Start Scene", getDecimalValue());
read(1);
addRow("End Scene", getDecimalValue());
read(2);
addRow("??", getHex0xValue(), "Always zeros?");
});
});
read(32);
addRow("Pattern Manager");
addDetails(()=> {
addPatternMaps();
});
read(8 * 3240);
addRow("Synth 1");
addDetails(()=> {
for (var s = 0; s < 8; s++) {
addPattern(s + 1);
}
});
read(8 * 3240);
addRow("Synth 2");
addDetails(()=> {
for (var s = 0; s < 8; s++) {
addPattern(s + 1);
}
});
read(8);
addRow("Synth 1 Settings", getHex0xValue(), "Contains mute data");
read(8);
addRow("Synth 2 Settings", getHex0xValue(), "Contains mute data");
read(1704 * 8);
addRow("Drum 1");
addDetails(()=> {
for (var d = 0; d < 8; d++) {
addDrumPattern(d);
}
});
read(1704 * 8);
addRow("Drum 2");
addDetails(()=> {
for (var d = 0; d < 8; d++) {
addDrumPattern(d);
}
});
read(1704 * 8);
addRow("Drum 3");
addDetails(()=> {
for (var d = 0; d < 8; d++) {
addDrumPattern(d);
}
});
read(1704 * 8);
addRow("Drum 4");
addDetails(()=> {
for (var d = 0; d < 8; d++) {
addDrumPattern(d);
}
});
read(4);
addRow("Drum Mute", getHex0xValue());
addDetails(()=>{
read(1);
addRow("Drum 1", getDecimalValue());
read(1);
addRow("Drum 2", getDecimalValue());
read(1);
addRow("Drum 3", getDecimalValue());
read(1);
addRow("Drum 4", getDecimalValue());
});
read(4);
addRow("Default Samples");
addDetails(()=>{
read(1);
addRow("Drum 1", getDecimalValue());
read(1);
addRow("Drum 2", getDecimalValue());
read(1);
addRow("Drum 3", getDecimalValue());
read(1);
addRow("Drum 4", getDecimalValue());
});
read(8 * 3240);
addRow("MIDI 1");
addDetails(()=> {
for (var s = 0; s < 8; s++) {
addPattern(s + 1);
}
});
read(8 * 3240);
addRow("MIDI 2");
addDetails(()=> {
for (var s = 0; s < 8; s++) {
addPattern(s + 1);
}
});
read(8);
addRow("MIDI 1 Settings", getHex0xValue(), "Contains mute data");
read(8);
addRow("MIDI 2 Settings", getHex0xValue(), "Contains mute data");
read(1);
addRow("Unknown6b", getDecimalValue(), "NOT always zero");
read(1);
addRow("Scale", getDecimalValue());
read(6);
addRow("Unknown6c", getHex0xValue());
read(340);
addRow('Synth 1 Patch', getStringValue().substring(0, 16));
addDetails(() => {
read(340);
addMemDump();
});
read(340);
addRow('Synth 2 Patch', getStringValue().substring(0, 16));
addDetails(() => {
read(340);
addMemDump();
});
read(44);
addRow("Drum Control/Mixer");
addDetails(()=>{
for (var c = 0; c < 4; c++) {
read(11);
addRow("Drum " + c, getHex0xValue());
addDetails(()=>{
read(1);
addRow("Sample", getDecimalValue());
read(1);
addRow("Level", getDecimalValue());
read(1);
addRow("Pan", getDecimalValue());
read(4);
addRow("Macros", getHex0xValue());
addDetails(()=>{
read(1);
addRow("M1", getDecimalValue());
read(1);
addRow("M2", getDecimalValue());
read(1);
addRow("M3", getDecimalValue());
read(1);
addRow("M4", getDecimalValue());
});
read(4);
addRow("??", getHex0xValue(),"Always zeros?");
});
}
});
read(32);
addRow("Unknown");
addDetails(()=>{
read(32);
addMemDump();
});
read(20);
addRow("Something");
addDetails(()=>{
for (var u = 0; u < 4; u++){
read(5);
addRow("?? " + (u + 1), getHex0xValue());
}
});
read(4);
addRow("Synth/Audio Mixer Level");
addDetails(()=> {
read(1);
addRow("Synth 1", getDecimalValue());
read(1);
addRow("Synth 2", getDecimalValue());
read(1);
addRow("Audio 1", getDecimalValue());
read(1);
addRow("Audio 2", getDecimalValue());
});
read(4);
addRow("Synth/Audio Mixer Pan");
addDetails(()=> {
read(1);
addRow("Synth 1", getDecimalValue());
read(1);
addRow("Synth 2", getDecimalValue());
read(1);
addRow("Audio 1", getDecimalValue());
read(1);
addRow("Audio 2", getDecimalValue());
});
read(1000);
addRow('Trailer', "", "Zeros for me, but seems like a hash? See Hello Tracks.");
addDetails(() => {
read(1000);
addMemDump();
});
read(0);
addRow("EOF", endOfFile(), "End of file check");
});
function addAutomation(n) {
read(32 * 6);
addRow(n);
addDetails(()=>{
setEndianness('big');
for (var s = 0; s < 32; s++) {
read(6);
addRow("Step " + (s + 1), getHexValue(), "(be)");
}
setEndianness('little');
});
}
function addPatternMaps() {
addPatternMap("Synth 1");
addPatternMap("Synth 2");
addPatternMap("MIDI 1");
addPatternMap("MIDI 2");
addPatternMap("Drum 1");
addPatternMap("Drum 2");
addPatternMap("Drum 3");
addPatternMap("Drum 4");
}
function addPatternMap(n) {
read(4);
addRow(n, getHex0xValue());
addDetails(()=> {
read(1);
addRow("Start", getDecimalValue());
read(1);
addRow("End", getDecimalValue());
read(2);
addRow("??", getHex0xValue(), "Always zeros?");
});
}
function addDrumPattern(d) {
read(1704);
addRow("Pattern " + (d + 1));
addDetails(() => {
setEndianness("big");
read(32);
addRow("Velocity", getHexValue(), "(be)");
read(32);
addRow("Probability", getHexValue(), "(be) 0 - 7 scale");
read(32);
addRow("Sample", getHexValue(), "(be)");
read(32);
addRow("Microsteps", getHexValue(), "(be)");
setEndianness("little");
read(1);
addRow("End Step", getDecimalValue());
read(1);
addRow("Start Step", getDecimalValue());
read(1);
addRow("SyncRate", getDecimalValue());
read(1);
addRow("Play order", getDecimalValue());
read(4);
addRow("??", getHex0xValue(), "Always zeros?");
read(32);
addRow("Trailer", getHex0xValue(), "Zeros for me, but seems like a hash? See Hello Tracks.");
read(1536);
addRow("Automation", "", "32 x 6 x (4 + 4)");
addDetails(()=>{
addAutomation("M1");
addAutomation("M2");
addAutomation("M3");
addAutomation("M4");
addAutomation("Level");
addAutomation("Pan");
addAutomation("Delay");
addAutomation("Reverb");
});
});
}
function addPattern(s) {
read(3240);
addRow("Pattern " + s);
addDetails(() => {
for (var i = 0; i < 32; i++) {
read(28);
addRow("Step " + (i + 1), getHex0xValue());
addDetails(() => {
read(4);
addRow('Settings', getHex0xValue());
addDetails(() => {
read(1);
addRow("Voice Flags", getBitsValue());
read(1);
addRow("Probability", getDecimalValue(), "0 - 7 scale");
read(2);
addRow("??", getHex0xValue(), "Always zeros?");
});
for (var ms = 1; ms < 7; ms++) {
read(4);
addRow('Note ' + ms, getHex0xValue());
addDetails(() => {
read(1);
addRow("Note", getDecimalValue());
readBits(7);
addRow("Gate", getDecimalValue());
readBits(1);
addRow("Tie in", getDecimalValue());
read(1);
addRow("Microstep", getDecimalValue());
read(1);
addRow("Velocity", getDecimalValue());
});
}
});
}
read(8);
addRow("Settings", getHex0xValue());
addDetails(()=>{
read(1);
addRow("End Step", getDecimalValue());
read(1);
addRow("Start Step", getDecimalValue());
read(1);
addRow("SyncRate", getDecimalValue());
read(1);
addRow("Play order", getDecimalValue());
read(4);
addRow("??", getHex0xValue(), "Always zeros?");
});
read(32);
addRow("Trailer", getHex0xValue(), "Zeros for me, but seems like a hash? See Hello Tracks.");
read(2304);
addRow('Automation', "", "32 x 6 x (8 + 4)");
addDetails(() => {
addAutomation("M1");
addAutomation("M2");
addAutomation("M3");
addAutomation("M4");
addAutomation("M5");
addAutomation("M6");
addAutomation("M7");
addAutomation("M8");
addAutomation("Level");
addAutomation("Pan");
addAutomation("Delay");
addAutomation("Reverb");
});
});
}
// this is incomplete
using System.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Melanchall.DryWetMidi.Composing;
[InlineArray(6)] unsafe struct Buffer6<T> { private T _element0; }
[InlineArray(8)] unsafe struct Buffer8<T> { private T _element0; }
[InlineArray(16)] unsafe struct Buffer16<T> { private T _element0; }
[InlineArray(32)] unsafe struct Buffer32<T> { private T _element0; }
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
unsafe struct Project
{
public fixed byte Header[4];
public int Filesize;
public int Unknown1;
public int RootNote;
public fixed byte Name[32];
public uint Unknown2;
public byte Tempo;
public byte Swing;
public byte Unknown3b;
public byte Unknown3c;
public long Unknown8a;
public Buffer16<Scene> Scenes;
public byte Start, End;
private ushort unknown5;
public Buffer8<PatternMap> PatternMap;
public Buffer8<Pattern> Synth1, Synth2;
public ulong Synth1Settings, Synth2Settings;
public Buffer8<DrumPattern> Drum1, Drum2, Drum3, Drum4;
public uint DrumMute;
public uint DefaultSamples;
public Buffer8<Pattern> Midi1, Midi2;
public ulong Midi1Settings, Midi2Settings;
public ulong Settings;
public Patch Synth1Patch, Synth2Patch;
public Mixer Mixer;
public fixed byte Trailer[1000];
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Scene
{
public PatternMap
Synth1,
Synth2,
Midi1,
Midi2,
Drum1,
Drum2,
Drum3,
Drum4;
public byte Enabled;
public byte Muted;
private ushort unknown1;
private uint Unknown2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct PatternMap
{
public byte Start, End;
private ushort unknown;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Pattern
{
public Buffer32<Step> Steps;
public ulong Settings;
public Buffer32<byte> Trailer;
public Automation Automation;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Step
{
public byte Flags;
public byte Probability;
ushort unknown;
public Buffer6<Voice> Voices;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Voice
{
public byte Note;
public byte GateAndTieIn;
public byte Microstep;
public byte Velocity;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Automation
{
public AutomationChannel
M1,
M2,
M3,
M4,
M5,
M6,
M7,
M8,
Level,
Pan,
Delay,
Reverb;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct DrumPattern
{
public Buffer32<byte> Velocity;
public Buffer32<byte> Probability;
public Buffer32<byte> Sample;
public Buffer32<byte> Microsteps;
public byte EndStep, StartStep, SyncRate, PlayOrder;
public uint Unknown;
public Buffer32<byte> Trailer;
public DrumAutomation Automation;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct DrumAutomation
{
public AutomationChannel
M1,
M2,
M3,
M4,
Level,
Pan,
Delay,
Reverb;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 340)]
unsafe struct Patch
{
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 104)]
unsafe struct Mixer
{
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 192)]
unsafe struct AutomationChannel
{
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment