ts-flp 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -330,11 +330,13 @@ function getEventFixedSize(eventId) {
330
330
  }
331
331
 
332
332
  // src/io/BinaryReader.ts
333
- var protobuf = __toESM(require("protobufjs/minimal.js"), 1);
333
+ var import_minimal = __toESM(require("protobufjs/minimal.js"), 1);
334
334
  var import_smart_buffer = require("smart-buffer");
335
335
  var BinaryReader = class {
336
336
  sb;
337
+ source;
337
338
  constructor(buffer) {
339
+ this.source = buffer;
338
340
  this.sb = import_smart_buffer.SmartBuffer.fromBuffer(buffer);
339
341
  }
340
342
  /**
@@ -432,8 +434,8 @@ var BinaryReader = class {
432
434
  * FL Studio uses this encoding for TEXT and DATA event sizes
433
435
  */
434
436
  readVarInt() {
435
- const remainingBuffer = this.sb.toBuffer().subarray(this.sb.readOffset);
436
- const reader = protobuf.Reader.create(remainingBuffer);
437
+ const remainingBuffer = this.source.subarray(this.sb.readOffset);
438
+ const reader = import_minimal.default.Reader.create(remainingBuffer);
437
439
  const value = reader.uint32();
438
440
  this.sb.readOffset += reader.pos;
439
441
  return value;
@@ -477,13 +479,13 @@ var BinaryReader = class {
477
479
  * Returns the internal buffer
478
480
  */
479
481
  toBuffer() {
480
- return this.sb.toBuffer();
482
+ return this.source;
481
483
  }
482
484
  };
483
485
 
484
486
  // src/io/BinaryWriter.ts
485
487
  var import_smart_buffer2 = require("smart-buffer");
486
- var protobuf2 = __toESM(require("protobufjs/minimal.js"), 1);
488
+ var import_minimal2 = __toESM(require("protobufjs/minimal.js"), 1);
487
489
  var BinaryWriter = class {
488
490
  sb;
489
491
  constructor() {
@@ -554,7 +556,7 @@ var BinaryWriter = class {
554
556
  * FL Studio uses this encoding for TEXT and DATA event sizes
555
557
  */
556
558
  writeVarInt(value) {
557
- const writer = protobuf2.Writer.create();
559
+ const writer = import_minimal2.default.Writer.create();
558
560
  writer.uint32(value);
559
561
  const encoded = writer.finish();
560
562
  this.sb.writeBuffer(Buffer.from(encoded));
@@ -580,7 +582,7 @@ var BinaryWriter = class {
580
582
  }
581
583
  };
582
584
  function encodeVarInt(value) {
583
- const writer = protobuf2.Writer.create();
585
+ const writer = import_minimal2.default.Writer.create();
584
586
  writer.uint32(value);
585
587
  return Buffer.from(writer.finish());
586
588
  }
@@ -628,6 +630,7 @@ function parseFlp(buffer) {
628
630
  const events = [];
629
631
  let flVersion = "0.0.0";
630
632
  let useUnicode = false;
633
+ let detectedVersion = false;
631
634
  const eventsStart = reader.tell();
632
635
  const eventsEnd = eventsStart + eventsSize;
633
636
  while (reader.tell() < eventsEnd) {
@@ -659,12 +662,17 @@ function parseFlp(buffer) {
659
662
  payload: Buffer.from(payload)
660
663
  };
661
664
  events.push(event);
662
- if (eventId === EVENT_ID.PROJECT_FL_VERSION) {
663
- flVersion = payload.toString("ascii").replace(/\0/g, "");
664
- const parts = flVersion.split(".").map((p) => parseInt(p, 10));
665
- const major = parts[0] ?? 0;
666
- const minor = parts[1] ?? 0;
667
- useUnicode = parts.length >= 2 && (major > 11 || major === 11 && minor >= 5);
665
+ if (eventId === EVENT_ID.PROJECT_FL_VERSION && !detectedVersion) {
666
+ const candidate = payload.toString("ascii").replace(/\0/g, "").trim();
667
+ const isSemverLike = /^\d+(\.\d+)+$/.test(candidate);
668
+ if (isSemverLike) {
669
+ flVersion = candidate;
670
+ const parts = flVersion.split(".").map((p) => parseInt(p, 10));
671
+ const major = parts[0] ?? 0;
672
+ const minor = parts[1] ?? 0;
673
+ useUnicode = parts.length >= 2 && (major > 11 || major === 11 && minor >= 5);
674
+ detectedVersion = true;
675
+ }
668
676
  }
669
677
  }
670
678
  let trailingBytes;
@@ -702,9 +710,13 @@ function serializeFlp(parsed) {
702
710
  }
703
711
  function serializeEvent(event) {
704
712
  const writer = new BinaryWriter();
705
- writer.writeU8(event.id);
706
- if (event.id >= TEXT) {
707
- writer.writeVarInt(event.payload.length);
713
+ if (event.header.length > 0) {
714
+ writer.writeBytes(event.header);
715
+ } else {
716
+ writer.writeU8(event.id);
717
+ if (event.id >= TEXT) {
718
+ writer.writeVarInt(event.payload.length);
719
+ }
708
720
  }
709
721
  writer.writeBytes(event.payload);
710
722
  return writer.toBuffer();
@@ -975,9 +987,7 @@ function parseVstPluginData(payload) {
975
987
  }
976
988
  const reader = new BinaryReader(payload);
977
989
  const typeMarker = reader.readU32LE();
978
- if (typeMarker !== 8 && typeMarker !== 10 && typeMarker !== 11) {
979
- return { name: null, vendor: null };
980
- }
990
+ void typeMarker;
981
991
  let name = null;
982
992
  let vendor = null;
983
993
  while (reader.remaining() >= 12) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts","../src/generated/events.generated.ts","../src/io/BinaryReader.ts","../src/io/BinaryWriter.ts","../src/parser/FlpParser.ts","../src/api/ProjectApi.ts"],"sourcesContent":["/**\n * ts-flp - A minimal TypeScript port of PyFLP for FL Studio .flp files\n *\n * This library provides conservative (non-destructive) parsing and patching\n * of FL Studio project files. It preserves all unknown data byte-for-byte.\n *\n * Features:\n * - Read and modify project metadata (name, description, artist, BPM)\n * - Read and modify timestamps (creation date, work time)\n * - List and remap sample paths\n * - List VST plugins (name and vendor)\n * - Conservative round-trip: unchanged files are byte-identical\n *\n * @example\n * ```typescript\n * import { parseFlp, serializeFlp, readProjectMeta, writeProjectMeta } from \"ts-flp\";\n * import * as fs from \"fs\";\n *\n * // Parse an FLP file\n * const buffer = fs.readFileSync(\"project.flp\");\n * const parsed = parseFlp(buffer);\n *\n * // Read metadata\n * const meta = readProjectMeta(parsed);\n * console.log(meta.name, meta.bpm);\n *\n * // Modify and save\n * const modified = writeProjectMeta(parsed, { bpm: 140 });\n * fs.writeFileSync(\"project_modified.flp\", serializeFlp(modified));\n * ```\n */\n\n// Re-export parser functions\nexport {\n createEvent, createNumberPayload, createTextPayload, findEvents,\n findFirstEvent, getEventNumber, getEventString, parseFlp, patchEvents, serializeFlp, type FlpEvent, type ParsedFlp\n} from \"./src/parser/FlpParser.js\";\n\n// Re-export API functions\nexport {\n getFlVersion,\n getPPQ,\n listPlugins,\n listSamples,\n readProjectMeta,\n readProjectTimeInfo,\n rewriteSamplePaths,\n writeProjectMeta,\n writeProjectTimeInfo,\n type PluginRef,\n type ProjectMeta,\n type ProjectTimeInfo,\n type SampleRef\n} from \"./src/api/ProjectApi.js\";\n\n// Re-export event constants\nexport { EVENT_ID, EVENT_KIND, getEventFixedSize, getEventKind } from \"./src/generated/events.generated.js\";\n\n// Re-export IO utilities for advanced usage\nexport { BinaryReader } from \"./src/io/BinaryReader.js\";\nexport { BinaryWriter, encodeVarInt, varIntSize } from \"./src/io/BinaryWriter.js\";\n\n","/**\r\n * Auto-generated event constants from PyFLP\r\n * FL Studio .flp file event IDs and types\r\n *\r\n * Event ID ranges:\r\n * - BYTE (0-63): 1 byte data\r\n * - WORD (64-127): 2 bytes data\r\n * - DWORD (128-191): 4 bytes data\r\n * - TEXT (192-207): Variable length strings (+ some IDs in DATA range)\r\n * - DATA (208+): Variable length binary data\r\n */\r\n\r\n// Base offsets for event ID ranges\r\nexport const BYTE = 0;\r\nexport const WORD = 64;\r\nexport const DWORD = 128;\r\nexport const TEXT = 192;\r\nexport const DATA = 208;\r\n\r\n// IDs for TEXT events that use UTF-16LE in newer FL versions\r\nexport const NEW_TEXT_IDS = [\r\n TEXT + 49, // ArrangementID.Name\r\n TEXT + 39, // DisplayGroupID.Name\r\n TEXT + 47, // TrackID.Name\r\n] as const;\r\n\r\n/**\r\n * Event IDs used by FL Studio project files\r\n * Based on PyFLP's event definitions\r\n */\r\nexport const EVENT_ID = {\r\n // === Project IDs (from project.py) ===\r\n PROJECT_LOOP_ACTIVE: 9,\r\n PROJECT_SHOW_INFO: 10,\r\n PROJECT__VOLUME: 12,\r\n PROJECT_PAN_LAW: 23,\r\n PROJECT_LICENSED: 28,\r\n PROJECT__TEMPO_COARSE: WORD + 2, // 66\r\n PROJECT_PITCH: WORD + 16, // 80\r\n PROJECT__TEMPO_FINE: WORD + 29, // 93\r\n PROJECT_CUR_GROUP_ID: DWORD + 18,\r\n PROJECT_TEMPO: DWORD + 28,\r\n PROJECT_FL_BUILD: DWORD + 31,\r\n PROJECT_TITLE: TEXT + 2, // 194\r\n PROJECT_COMMENTS: TEXT + 3, // 195\r\n PROJECT_URL: TEXT + 5, // 197\r\n PROJECT__RTF_COMMENTS: TEXT + 6, // 198\r\n PROJECT_FL_VERSION: TEXT + 7, // 199\r\n PROJECT_LICENSEE: TEXT + 8, // 200\r\n PROJECT_DATA_PATH: TEXT + 10, // 202\r\n PROJECT_GENRE: TEXT + 14, // 206\r\n PROJECT_ARTISTS: TEXT + 15, // 207\r\n PROJECT_TIMESTAMP: DATA + 29, // 237\r\n\r\n // === Channel IDs (from channel.py) ===\r\n CHANNEL_IS_ENABLED: 0,\r\n CHANNEL__VOL_BYTE: 2,\r\n CHANNEL__PAN_BYTE: 3,\r\n CHANNEL_ZIPPED: 15,\r\n CHANNEL_PING_PONG_LOOP: 20,\r\n CHANNEL_TYPE: 21,\r\n CHANNEL_ROUTED_TO: 22,\r\n CHANNEL_IS_LOCKED: 32,\r\n CHANNEL_NEW: WORD,\r\n CHANNEL_FREQ_TILT: WORD + 5, // 69\r\n CHANNEL_FX_FLAGS: WORD + 6, // 70\r\n CHANNEL_CUTOFF: WORD + 7, // 71\r\n CHANNEL__VOL_WORD: WORD + 8, // 72\r\n CHANNEL__PAN_WORD: WORD + 9, // 73\r\n CHANNEL_PREAMP: WORD + 10, // 74\r\n CHANNEL_FADE_OUT: WORD + 11, // 75\r\n CHANNEL_FADE_IN: WORD + 12, // 76\r\n CHANNEL_RESONANCE: WORD + 19, // 83\r\n CHANNEL_STEREO_DELAY: WORD + 21, // 85\r\n CHANNEL_POGO: WORD + 22, // 86\r\n CHANNEL_TIME_SHIFT: WORD + 25, // 89\r\n CHANNEL_CHILDREN: WORD + 30, // 94\r\n CHANNEL_SWING: WORD + 33, // 97\r\n CHANNEL_RING_MOD: DWORD + 3,\r\n CHANNEL_CUT_GROUP: DWORD + 4,\r\n CHANNEL_ROOT_NOTE: DWORD + 7,\r\n CHANNEL_DELAY_MOD_XY: DWORD + 10,\r\n CHANNEL_REVERB: DWORD + 11,\r\n CHANNEL__STRETCH_TIME: DWORD + 12,\r\n CHANNEL_FINE_TUNE: DWORD + 14,\r\n CHANNEL_SAMPLER_FLAGS: DWORD + 15,\r\n CHANNEL_LAYER_FLAGS: DWORD + 16,\r\n CHANNEL_GROUP_NUM: DWORD + 17,\r\n CHANNEL_AU_SAMPLE_RATE: DWORD + 25,\r\n CHANNEL__NAME: TEXT,\r\n CHANNEL_SAMPLE_PATH: TEXT + 4, // 196\r\n CHANNEL_DELAY: DATA + 1, // 209\r\n CHANNEL_PARAMETERS: DATA + 7, // 215\r\n CHANNEL_ENVELOPE_LFO: DATA + 10, // 218\r\n CHANNEL_LEVELS: DATA + 11, // 219\r\n CHANNEL_POLYPHONY: DATA + 13, // 221\r\n CHANNEL_TRACKING: DATA + 20, // 228\r\n CHANNEL_LEVEL_ADJUSTS: DATA + 21, // 229\r\n CHANNEL_AUTOMATION: DATA + 26, // 234\r\n\r\n // === Plugin IDs (from plugin.py) ===\r\n PLUGIN_COLOR: DWORD,\r\n PLUGIN_ICON: DWORD + 27,\r\n PLUGIN_INTERNAL_NAME: TEXT + 9, // 201\r\n PLUGIN_NAME: TEXT + 11, // 203\r\n PLUGIN_WRAPPER: DATA + 4, // 212\r\n PLUGIN_DATA: DATA + 5, // 213\r\n\r\n // === Display Group IDs ===\r\n DISPLAY_GROUP_NAME: TEXT + 39, // 231\r\n\r\n // === Rack IDs ===\r\n RACK_SWING: 11,\r\n RACK__FIT_TO_STEPS: 13,\r\n RACK_WINDOW_HEIGHT: DWORD + 5,\r\n\r\n // === Mixer IDs (from mixer.py) ===\r\n MIXER_SLOT_INDEX: WORD + 34, // 98\r\n MIXER_INSERT_ICON: WORD + 31, // 95\r\n MIXER_INSERT_OUTPUT: DWORD + 19,\r\n MIXER_INSERT_COLOR: DWORD + 21,\r\n MIXER_INSERT_INPUT: DWORD + 26,\r\n MIXER_INSERT_NAME: TEXT + 12, // 204\r\n MIXER_INSERT_ROUTING: DATA + 27, // 235\r\n MIXER_INSERT_FLAGS: DATA + 28, // 236\r\n} as const;\r\n\r\n/**\r\n * Event data type classification\r\n */\r\nexport type EventKind =\r\n | \"u8\"\r\n | \"i8\"\r\n | \"u16\"\r\n | \"i16\"\r\n | \"u32\"\r\n | \"i32\"\r\n | \"f32\"\r\n | \"text\"\r\n | \"data\"\r\n | \"unknown\";\r\n\r\n/**\r\n * Maps event IDs to their data type\r\n * Used for proper serialization/deserialization\r\n */\r\nexport const EVENT_KIND: Record<number, EventKind> = {\r\n // Project events\r\n [EVENT_ID.PROJECT_LOOP_ACTIVE]: \"u8\",\r\n [EVENT_ID.PROJECT_SHOW_INFO]: \"u8\",\r\n [EVENT_ID.PROJECT__VOLUME]: \"u8\",\r\n [EVENT_ID.PROJECT_PAN_LAW]: \"u8\",\r\n [EVENT_ID.PROJECT_LICENSED]: \"u8\",\r\n [EVENT_ID.PROJECT__TEMPO_COARSE]: \"u16\",\r\n [EVENT_ID.PROJECT_PITCH]: \"i16\",\r\n [EVENT_ID.PROJECT__TEMPO_FINE]: \"u16\",\r\n [EVENT_ID.PROJECT_CUR_GROUP_ID]: \"i32\",\r\n [EVENT_ID.PROJECT_TEMPO]: \"u32\",\r\n [EVENT_ID.PROJECT_FL_BUILD]: \"u32\",\r\n [EVENT_ID.PROJECT_TITLE]: \"text\",\r\n [EVENT_ID.PROJECT_COMMENTS]: \"text\",\r\n [EVENT_ID.PROJECT_URL]: \"text\",\r\n [EVENT_ID.PROJECT__RTF_COMMENTS]: \"text\",\r\n [EVENT_ID.PROJECT_FL_VERSION]: \"text\",\r\n [EVENT_ID.PROJECT_LICENSEE]: \"text\",\r\n [EVENT_ID.PROJECT_DATA_PATH]: \"text\",\r\n [EVENT_ID.PROJECT_GENRE]: \"text\",\r\n [EVENT_ID.PROJECT_ARTISTS]: \"text\",\r\n [EVENT_ID.PROJECT_TIMESTAMP]: \"data\",\r\n\r\n // Channel events\r\n [EVENT_ID.CHANNEL_IS_ENABLED]: \"u8\",\r\n [EVENT_ID.CHANNEL__VOL_BYTE]: \"u8\",\r\n [EVENT_ID.CHANNEL__PAN_BYTE]: \"u8\",\r\n [EVENT_ID.CHANNEL_ZIPPED]: \"u8\",\r\n [EVENT_ID.CHANNEL_PING_PONG_LOOP]: \"u8\",\r\n [EVENT_ID.CHANNEL_TYPE]: \"u8\",\r\n [EVENT_ID.CHANNEL_ROUTED_TO]: \"i8\",\r\n [EVENT_ID.CHANNEL_IS_LOCKED]: \"u8\",\r\n [EVENT_ID.CHANNEL_NEW]: \"u16\",\r\n [EVENT_ID.CHANNEL_FREQ_TILT]: \"u16\",\r\n [EVENT_ID.CHANNEL_FX_FLAGS]: \"u16\",\r\n [EVENT_ID.CHANNEL_CUTOFF]: \"u16\",\r\n [EVENT_ID.CHANNEL__VOL_WORD]: \"u16\",\r\n [EVENT_ID.CHANNEL__PAN_WORD]: \"u16\",\r\n [EVENT_ID.CHANNEL_PREAMP]: \"u16\",\r\n [EVENT_ID.CHANNEL_FADE_OUT]: \"u16\",\r\n [EVENT_ID.CHANNEL_FADE_IN]: \"u16\",\r\n [EVENT_ID.CHANNEL_RESONANCE]: \"u16\",\r\n [EVENT_ID.CHANNEL_STEREO_DELAY]: \"u16\",\r\n [EVENT_ID.CHANNEL_POGO]: \"u16\",\r\n [EVENT_ID.CHANNEL_TIME_SHIFT]: \"u16\",\r\n [EVENT_ID.CHANNEL_CHILDREN]: \"u16\",\r\n [EVENT_ID.CHANNEL_SWING]: \"u16\",\r\n [EVENT_ID.CHANNEL_RING_MOD]: \"u32\",\r\n [EVENT_ID.CHANNEL_CUT_GROUP]: \"u32\",\r\n [EVENT_ID.CHANNEL_ROOT_NOTE]: \"u32\",\r\n [EVENT_ID.CHANNEL_DELAY_MOD_XY]: \"u32\",\r\n [EVENT_ID.CHANNEL_REVERB]: \"u32\",\r\n [EVENT_ID.CHANNEL__STRETCH_TIME]: \"f32\",\r\n [EVENT_ID.CHANNEL_FINE_TUNE]: \"i32\",\r\n [EVENT_ID.CHANNEL_SAMPLER_FLAGS]: \"u32\",\r\n [EVENT_ID.CHANNEL_LAYER_FLAGS]: \"u32\",\r\n [EVENT_ID.CHANNEL_GROUP_NUM]: \"i32\",\r\n [EVENT_ID.CHANNEL_AU_SAMPLE_RATE]: \"u32\",\r\n [EVENT_ID.CHANNEL__NAME]: \"text\",\r\n [EVENT_ID.CHANNEL_SAMPLE_PATH]: \"text\",\r\n [EVENT_ID.CHANNEL_DELAY]: \"data\",\r\n [EVENT_ID.CHANNEL_PARAMETERS]: \"data\",\r\n [EVENT_ID.CHANNEL_ENVELOPE_LFO]: \"data\",\r\n [EVENT_ID.CHANNEL_LEVELS]: \"data\",\r\n [EVENT_ID.CHANNEL_POLYPHONY]: \"data\",\r\n [EVENT_ID.CHANNEL_TRACKING]: \"data\",\r\n [EVENT_ID.CHANNEL_LEVEL_ADJUSTS]: \"data\",\r\n [EVENT_ID.CHANNEL_AUTOMATION]: \"data\",\r\n\r\n // Plugin events\r\n [EVENT_ID.PLUGIN_COLOR]: \"u32\",\r\n [EVENT_ID.PLUGIN_ICON]: \"u32\",\r\n [EVENT_ID.PLUGIN_INTERNAL_NAME]: \"text\",\r\n [EVENT_ID.PLUGIN_NAME]: \"text\",\r\n [EVENT_ID.PLUGIN_WRAPPER]: \"data\",\r\n [EVENT_ID.PLUGIN_DATA]: \"data\",\r\n\r\n // Display Group events\r\n [EVENT_ID.DISPLAY_GROUP_NAME]: \"data\",\r\n\r\n // Rack events\r\n [EVENT_ID.RACK_SWING]: \"u8\",\r\n [EVENT_ID.RACK__FIT_TO_STEPS]: \"u8\",\r\n [EVENT_ID.RACK_WINDOW_HEIGHT]: \"u32\",\r\n\r\n // Mixer events\r\n [EVENT_ID.MIXER_SLOT_INDEX]: \"u16\",\r\n [EVENT_ID.MIXER_INSERT_ICON]: \"i16\",\r\n [EVENT_ID.MIXER_INSERT_OUTPUT]: \"i32\",\r\n [EVENT_ID.MIXER_INSERT_COLOR]: \"u32\",\r\n [EVENT_ID.MIXER_INSERT_INPUT]: \"i32\",\r\n [EVENT_ID.MIXER_INSERT_NAME]: \"text\",\r\n [EVENT_ID.MIXER_INSERT_ROUTING]: \"data\",\r\n [EVENT_ID.MIXER_INSERT_FLAGS]: \"data\",\r\n\r\n};\r\n\r\n/**\r\n * Determines the event kind based on ID\r\n * Falls back to range-based detection if not explicitly mapped\r\n */\r\nexport function getEventKind(eventId: number): EventKind {\r\n // Check explicit mapping first\r\n const mapped = EVENT_KIND[eventId];\r\n if (mapped !== undefined) {\r\n return mapped;\r\n }\r\n\r\n // Fall back to range-based detection\r\n if (eventId < WORD) {\r\n return \"u8\";\r\n } else if (eventId < DWORD) {\r\n return \"u16\";\r\n } else if (eventId < TEXT) {\r\n return \"u32\";\r\n } else if (eventId < DATA || NEW_TEXT_IDS.includes(eventId as 241 | 231 | 239)) {\r\n return \"text\";\r\n } else {\r\n return \"data\";\r\n }\r\n}\r\n\r\n/**\r\n * Returns the fixed size for BYTE/WORD/DWORD events\r\n * Returns -1 for variable-length events (TEXT/DATA)\r\n */\r\nexport function getEventFixedSize(eventId: number): number {\r\n if (eventId < WORD) {\r\n return 1;\r\n } else if (eventId < DWORD) {\r\n return 2;\r\n } else if (eventId < TEXT) {\r\n return 4;\r\n }\r\n return -1; // Variable length\r\n}\r\n","/**\n * Binary reader for FL Studio .flp files\n * Uses smart-buffer for binary reading and protobufjs for VarInt decoding\n */\n\nimport * as protobuf from \"protobufjs/minimal.js\";\nimport { SmartBuffer } from \"smart-buffer\";\n\nexport class BinaryReader {\n private sb: SmartBuffer;\n\n constructor(buffer: Buffer) {\n this.sb = SmartBuffer.fromBuffer(buffer);\n }\n\n /**\n * Returns current read position\n */\n tell(): number {\n return this.sb.readOffset;\n }\n\n /**\n * Seeks to an absolute position\n */\n seek(pos: number): void {\n this.sb.readOffset = pos;\n }\n\n /**\n * Seeks relative to current position\n */\n skip(offset: number): void {\n this.sb.readOffset += offset;\n }\n\n /**\n * Returns total buffer length\n */\n length(): number {\n return this.sb.length;\n }\n\n /**\n * Returns remaining bytes to read\n */\n remaining(): number {\n return this.sb.remaining();\n }\n\n /**\n * Returns true if there are more bytes to read\n */\n hasMore(): boolean {\n return this.sb.remaining() > 0;\n }\n\n /**\n * Reads an unsigned 8-bit integer\n */\n readU8(): number {\n return this.sb.readUInt8();\n }\n\n /**\n * Reads a signed 8-bit integer\n */\n readI8(): number {\n return this.sb.readInt8();\n }\n\n /**\n * Reads an unsigned 16-bit integer (little-endian)\n */\n readU16LE(): number {\n return this.sb.readUInt16LE();\n }\n\n /**\n * Reads a signed 16-bit integer (little-endian)\n */\n readI16LE(): number {\n return this.sb.readInt16LE();\n }\n\n /**\n * Reads an unsigned 32-bit integer (little-endian)\n */\n readU32LE(): number {\n return this.sb.readUInt32LE();\n }\n\n /**\n * Reads a signed 32-bit integer (little-endian)\n */\n readI32LE(): number {\n return this.sb.readInt32LE();\n }\n\n /**\n * Reads a 32-bit float (little-endian)\n */\n readF32LE(): number {\n return this.sb.readFloatLE();\n }\n\n /**\n * Reads a 64-bit float (little-endian)\n */\n readF64LE(): number {\n return this.sb.readDoubleLE();\n }\n\n /**\n * Reads raw bytes\n */\n readBytes(length: number): Buffer {\n return this.sb.readBuffer(length);\n }\n\n /**\n * Reads a VarInt (variable-length integer) using protobufjs\n * FL Studio uses this encoding for TEXT and DATA event sizes\n */\n readVarInt(): number {\n // Get remaining buffer from current position\n const remainingBuffer = this.sb.toBuffer().subarray(this.sb.readOffset);\n const reader = protobuf.Reader.create(remainingBuffer);\n const value = reader.uint32();\n // Advance our position by how many bytes protobufjs consumed\n this.sb.readOffset += reader.pos;\n return value;\n }\n\n /**\n * Reads an ASCII string (null-terminated)\n */\n readAsciiString(length: number): string {\n const str = this.sb.readString(length, \"ascii\");\n // Remove null terminator if present\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n\n /**\n * Reads a UTF-16LE string (null-terminated)\n */\n readUtf16LEString(length: number): string {\n const bytes = this.sb.readBuffer(length);\n const str = bytes.toString(\"utf16le\");\n // Remove null terminator if present\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n\n /**\n * Peeks at bytes without advancing position\n */\n peek(length: number): Buffer {\n const pos = this.sb.readOffset;\n const data = this.sb.readBuffer(length);\n this.sb.readOffset = pos;\n return data;\n }\n\n /**\n * Peeks at a single byte without advancing position\n */\n peekU8(): number {\n const pos = this.sb.readOffset;\n const value = this.sb.readUInt8();\n this.sb.readOffset = pos;\n return value;\n }\n\n /**\n * Returns the internal buffer\n */\n toBuffer(): Buffer {\n return this.sb.toBuffer();\n }\n}\n","/**\n * Binary writer for FL Studio .flp files\n * Uses smart-buffer for binary writing and protobufjs for VarInt encoding\n */\n\nimport { SmartBuffer } from \"smart-buffer\";\nimport * as protobuf from \"protobufjs/minimal.js\";\n\nexport class BinaryWriter {\n private sb: SmartBuffer;\n\n constructor() {\n this.sb = new SmartBuffer();\n }\n\n /**\n * Returns current write position (total bytes written)\n */\n tell(): number {\n return this.sb.writeOffset;\n }\n\n /**\n * Writes an unsigned 8-bit integer\n */\n writeU8(value: number): void {\n this.sb.writeUInt8(value & 0xff);\n }\n\n /**\n * Writes a signed 8-bit integer\n */\n writeI8(value: number): void {\n this.sb.writeInt8(value);\n }\n\n /**\n * Writes an unsigned 16-bit integer (little-endian)\n */\n writeU16LE(value: number): void {\n this.sb.writeUInt16LE(value & 0xffff);\n }\n\n /**\n * Writes a signed 16-bit integer (little-endian)\n */\n writeI16LE(value: number): void {\n this.sb.writeInt16LE(value);\n }\n\n /**\n * Writes an unsigned 32-bit integer (little-endian)\n */\n writeU32LE(value: number): void {\n this.sb.writeUInt32LE(value >>> 0);\n }\n\n /**\n * Writes a signed 32-bit integer (little-endian)\n */\n writeI32LE(value: number): void {\n this.sb.writeInt32LE(value);\n }\n\n /**\n * Writes a 32-bit float (little-endian)\n */\n writeF32LE(value: number): void {\n this.sb.writeFloatLE(value);\n }\n\n /**\n * Writes a 64-bit float (little-endian)\n */\n writeF64LE(value: number): void {\n this.sb.writeDoubleLE(value);\n }\n\n /**\n * Writes raw bytes\n */\n writeBytes(data: Buffer): void {\n this.sb.writeBuffer(data);\n }\n\n /**\n * Writes a VarInt (variable-length integer) using protobufjs\n * FL Studio uses this encoding for TEXT and DATA event sizes\n */\n writeVarInt(value: number): void {\n const writer = protobuf.Writer.create();\n writer.uint32(value);\n const encoded = writer.finish();\n this.sb.writeBuffer(Buffer.from(encoded));\n }\n\n /**\n * Writes an ASCII string (null-terminated)\n */\n writeAsciiString(str: string): void {\n this.sb.writeString(str + \"\\0\", \"ascii\");\n }\n\n /**\n * Writes a UTF-16LE string (null-terminated)\n */\n writeUtf16LEString(str: string): void {\n const buf = Buffer.from(str + \"\\0\", \"utf16le\");\n this.sb.writeBuffer(buf);\n }\n\n /**\n * Gets the final buffer\n */\n toBuffer(): Buffer {\n return this.sb.toBuffer();\n }\n}\n\n/**\n * Encodes a VarInt to a Buffer using protobufjs (standalone utility)\n */\nexport function encodeVarInt(value: number): Buffer {\n const writer = protobuf.Writer.create();\n writer.uint32(value);\n return Buffer.from(writer.finish());\n}\n\n/**\n * Calculates the byte size needed for a VarInt\n */\nexport function varIntSize(value: number): number {\n if (value < 0x80) return 1;\n if (value < 0x4000) return 2;\n if (value < 0x200000) return 3;\n if (value < 0x10000000) return 4;\n return 5;\n}\n","/**\n * FL Studio .flp file parser and serializer\n * Conservative patch approach: preserves all unknown data byte-for-byte\n */\n\nimport { DWORD, EVENT_ID, TEXT, WORD, getEventKind } from \"../generated/events.generated.js\";\nimport { BinaryReader } from \"../io/BinaryReader.js\";\nimport { BinaryWriter } from \"../io/BinaryWriter.js\";\n\n/**\n * Represents a single event in an FLP file\n * Stores raw bytes to enable conservative patching\n */\nexport interface FlpEvent {\n /** Event ID (0-255) */\n id: number;\n /** Data type classification */\n kind: \"u8\" | \"i8\" | \"u16\" | \"i16\" | \"u32\" | \"i32\" | \"f32\" | \"text\" | \"data\" | \"unknown\";\n /** Original header bytes (ID + size for variable-length events) */\n header: Buffer;\n /** Event payload data */\n payload: Buffer;\n}\n\n/**\n * Represents a parsed FLP file\n * Contains all data needed for conservative round-trip\n */\nexport interface ParsedFlp {\n /** FLhd chunk (header) - 14 bytes total */\n headerChunkBytes: Buffer;\n /** FLdt chunk header (magic + size placeholder) - 8 bytes */\n fldtHeaderBytes: Buffer;\n /** All events from the FLdt chunk */\n events: FlpEvent[];\n /** Any trailing bytes after events (should be empty) */\n trailingBytes: Buffer | undefined;\n /** Detected FL version for string encoding */\n flVersion: string;\n /** Whether to use UTF-16LE for strings (FL >= 11.5) */\n useUnicode: boolean;\n}\n\n/**\n * Header structure constants\n */\nconst FLP_HEADER_MAGIC = \"FLhd\";\nconst FLP_DATA_MAGIC = \"FLdt\";\nconst FLP_HEADER_SIZE = 6; // Expected header chunk size\n\n/**\n * Parses an FL Studio project file\n *\n * @param buffer - Raw file data\n * @returns Parsed FLP structure\n * @throws Error if file is corrupted or invalid\n */\nexport function parseFlp(buffer: Buffer): ParsedFlp {\n const reader = new BinaryReader(buffer);\n\n // Read FLhd header chunk\n const headerMagic = reader.readBytes(4).toString(\"ascii\");\n if (headerMagic !== FLP_HEADER_MAGIC) {\n throw new Error(`Invalid header magic: expected 'FLhd', got '${headerMagic}'`);\n }\n\n const headerSize = reader.readU32LE();\n if (headerSize !== FLP_HEADER_SIZE) {\n throw new Error(`Invalid header size: expected ${FLP_HEADER_SIZE}, got ${headerSize}`);\n }\n\n const format = reader.readI16LE();\n const channelCount = reader.readU16LE();\n const ppq = reader.readU16LE();\n\n // Validate format (0 = normal project)\n if (format < -1 || format > 0x50) {\n throw new Error(`Invalid file format: ${format}`);\n }\n\n // Store complete header chunk (14 bytes)\n const headerChunkBytes = buffer.subarray(0, 14);\n\n // Read FLdt data chunk header\n const dataMagic = reader.readBytes(4).toString(\"ascii\");\n if (dataMagic !== FLP_DATA_MAGIC) {\n throw new Error(`Invalid data magic: expected 'FLdt', got '${dataMagic}'`);\n }\n\n const eventsSize = reader.readU32LE();\n\n // Validate events size\n const expectedFileSize = 22 + eventsSize; // 14 (FLhd) + 8 (FLdt header) + events\n if (buffer.length !== expectedFileSize) {\n throw new Error(\n `Data chunk size mismatch: expected file size ${expectedFileSize}, got ${buffer.length}`\n );\n }\n\n // Store FLdt header (8 bytes)\n const fldtHeaderBytes = buffer.subarray(14, 22);\n\n // Parse events\n const events: FlpEvent[] = [];\n let flVersion = \"0.0.0\";\n let useUnicode = false;\n\n const eventsStart = reader.tell();\n const eventsEnd = eventsStart + eventsSize;\n\n while (reader.tell() < eventsEnd) {\n const eventStart = reader.tell();\n const eventId = reader.readU8();\n\n let payloadSize: number;\n let headerSize: number;\n\n // Determine payload size based on event ID range\n if (eventId < WORD) {\n // BYTE range: 1 byte payload\n payloadSize = 1;\n headerSize = 1; // Just the ID\n } else if (eventId < DWORD) {\n // WORD range: 2 bytes payload\n payloadSize = 2;\n headerSize = 1;\n } else if (eventId < TEXT) {\n // DWORD range: 4 bytes payload\n payloadSize = 4;\n headerSize = 1;\n } else {\n // TEXT/DATA range: VarInt-encoded size\n const sizeStart = reader.tell();\n payloadSize = reader.readVarInt();\n headerSize = 1 + (reader.tell() - sizeStart); // ID + VarInt bytes\n }\n\n // Read payload\n const payload = reader.readBytes(payloadSize);\n\n // Extract header from original buffer for byte-perfect round-trip\n const header = buffer.subarray(eventStart, eventStart + headerSize);\n\n // Determine event kind\n const kind = getEventKind(eventId);\n\n const event: FlpEvent = {\n id: eventId,\n kind,\n header: Buffer.from(header),\n payload: Buffer.from(payload),\n };\n\n events.push(event);\n\n // Detect FL version from FLVersion event\n if (eventId === EVENT_ID.PROJECT_FL_VERSION) {\n flVersion = payload.toString(\"ascii\").replace(/\\0/g, \"\");\n const parts = flVersion.split(\".\").map((p) => parseInt(p, 10));\n const major = parts[0] ?? 0;\n const minor = parts[1] ?? 0;\n useUnicode = parts.length >= 2 && (major > 11 || (major === 11 && minor >= 5));\n }\n }\n\n // Check for trailing bytes\n let trailingBytes: Buffer | undefined;\n if (reader.remaining() > 0) {\n trailingBytes = reader.readBytes(reader.remaining());\n }\n\n return {\n headerChunkBytes: Buffer.from(headerChunkBytes),\n fldtHeaderBytes: Buffer.from(fldtHeaderBytes),\n events,\n trailingBytes,\n flVersion,\n useUnicode,\n };\n}\n\n/**\n * Serializes a parsed FLP back to a buffer\n * Recalculates FLdt chunk size if events have changed\n *\n * @param parsed - Parsed FLP structure\n * @returns Serialized buffer\n */\nexport function serializeFlp(parsed: ParsedFlp): Buffer {\n const writer = new BinaryWriter();\n\n // Write FLhd header chunk (preserved byte-for-byte)\n writer.writeBytes(parsed.headerChunkBytes);\n\n // Calculate total events size\n let eventsSize = 0;\n for (const event of parsed.events) {\n eventsSize += serializeEvent(event).length;\n }\n\n // Add trailing bytes if present\n if (parsed.trailingBytes) {\n eventsSize += parsed.trailingBytes.length;\n }\n\n // Write FLdt header with updated size\n writer.writeBytes(Buffer.from(FLP_DATA_MAGIC, \"ascii\"));\n writer.writeU32LE(eventsSize);\n\n // Write all events\n for (const event of parsed.events) {\n writer.writeBytes(serializeEvent(event));\n }\n\n // Write trailing bytes if present\n if (parsed.trailingBytes) {\n writer.writeBytes(parsed.trailingBytes);\n }\n\n return writer.toBuffer();\n}\n\n/**\n * Serializes a single event to bytes\n * Uses original header for unchanged events, recalculates for modified ones\n */\nfunction serializeEvent(event: FlpEvent): Buffer {\n const writer = new BinaryWriter();\n\n // Write event ID\n writer.writeU8(event.id);\n\n // For TEXT/DATA events, we need to write the VarInt size\n if (event.id >= TEXT) {\n writer.writeVarInt(event.payload.length);\n }\n\n // Write payload\n writer.writeBytes(event.payload);\n\n return writer.toBuffer();\n}\n\n/**\n * Creates a new event with the given ID and payload\n */\nexport function createEvent(id: number, payload: Buffer): FlpEvent {\n const kind = getEventKind(id);\n\n // Build header\n const headerWriter = new BinaryWriter();\n headerWriter.writeU8(id);\n\n if (id >= TEXT) {\n headerWriter.writeVarInt(payload.length);\n }\n\n return {\n id,\n kind,\n header: headerWriter.toBuffer(),\n payload: Buffer.from(payload),\n };\n}\n\n/**\n * Applies a patcher function to all events\n * Returns a new ParsedFlp with patched events\n *\n * @param parsed - Original parsed FLP\n * @param patcher - Function that transforms each event\n * @returns New ParsedFlp with patched events\n */\nexport function patchEvents(\n parsed: ParsedFlp,\n patcher: (event: FlpEvent, index: number) => FlpEvent\n): ParsedFlp {\n const patchedEvents = parsed.events.map((event, index) => {\n const patched = patcher(event, index);\n\n // If payload changed, recalculate header for TEXT/DATA events\n if (patched.payload !== event.payload && patched.id >= TEXT) {\n const headerWriter = new BinaryWriter();\n headerWriter.writeU8(patched.id);\n headerWriter.writeVarInt(patched.payload.length);\n return {\n ...patched,\n header: headerWriter.toBuffer(),\n };\n }\n\n return patched;\n });\n\n return {\n ...parsed,\n events: patchedEvents,\n };\n}\n\n/**\n * Finds all events with a specific ID\n */\nexport function findEvents(parsed: ParsedFlp, eventId: number): FlpEvent[] {\n return parsed.events.filter((e) => e.id === eventId);\n}\n\n/**\n * Finds the first event with a specific ID\n */\nexport function findFirstEvent(parsed: ParsedFlp, eventId: number): FlpEvent | undefined {\n return parsed.events.find((e) => e.id === eventId);\n}\n\n/**\n * Gets event payload as a string (handles encoding)\n */\nexport function getEventString(event: FlpEvent, useUnicode: boolean): string {\n if (event.kind !== \"text\") {\n throw new Error(`Event ${event.id} is not a text event`);\n }\n\n if (useUnicode) {\n const str = event.payload.toString(\"utf16le\");\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n } else {\n const str = event.payload.toString(\"ascii\");\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n}\n\n/**\n * Creates a text payload with proper encoding\n */\nexport function createTextPayload(text: string, useUnicode: boolean): Buffer {\n if (useUnicode) {\n return Buffer.from(text + \"\\0\", \"utf16le\");\n } else {\n return Buffer.from(text + \"\\0\", \"ascii\");\n }\n}\n\n/**\n * Gets event payload as a number\n */\nexport function getEventNumber(event: FlpEvent): number {\n const reader = new BinaryReader(event.payload);\n\n switch (event.kind) {\n case \"u8\":\n return reader.readU8();\n case \"i8\":\n return reader.readI8();\n case \"u16\":\n return reader.readU16LE();\n case \"i16\":\n return reader.readI16LE();\n case \"u32\":\n return reader.readU32LE();\n case \"i32\":\n return reader.readI32LE();\n case \"f32\":\n return reader.readF32LE();\n default:\n throw new Error(`Cannot get number from ${event.kind} event`);\n }\n}\n\n/**\n * Creates a numeric payload\n */\nexport function createNumberPayload(\n value: number,\n kind: \"u8\" | \"i8\" | \"u16\" | \"i16\" | \"u32\" | \"i32\" | \"f32\"\n): Buffer {\n const writer = new BinaryWriter();\n\n switch (kind) {\n case \"u8\":\n writer.writeU8(value);\n break;\n case \"i8\":\n writer.writeI8(value);\n break;\n case \"u16\":\n writer.writeU16LE(value);\n break;\n case \"i16\":\n writer.writeI16LE(value);\n break;\n case \"u32\":\n writer.writeU32LE(value);\n break;\n case \"i32\":\n writer.writeI32LE(value);\n break;\n case \"f32\":\n writer.writeF32LE(value);\n break;\n }\n\n return writer.toBuffer();\n}\n","/**\n * High-level API for reading and modifying FL Studio project files\n * Provides typed access to project metadata, samples, plugins, and time info\n */\n\nimport { EVENT_ID } from \"../generated/events.generated.js\";\nimport { BinaryReader } from \"../io/BinaryReader.js\";\nimport { BinaryWriter } from \"../io/BinaryWriter.js\";\nimport {\n createNumberPayload,\n createTextPayload,\n findFirstEvent,\n type FlpEvent,\n getEventNumber,\n getEventString,\n type ParsedFlp,\n patchEvents,\n} from \"../parser/FlpParser.js\";\n\n// ============================================================================\n// Project Metadata\n// ============================================================================\n\n/**\n * Project metadata (name, description, artist, genre, BPM)\n */\nexport interface ProjectMeta {\n name: string | null;\n description: string | null;\n artist: string | null;\n genre: string | null;\n bpm: number | null;\n}\n\n/**\n * Reads project metadata from a parsed FLP\n */\nexport function readProjectMeta(parsed: ParsedFlp): ProjectMeta {\n const titleEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TITLE);\n const commentsEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_COMMENTS);\n const artistsEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_ARTISTS);\n const genreEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_GENRE);\n const tempoEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TEMPO);\n\n return {\n name: titleEvent ? getEventString(titleEvent, parsed.useUnicode) : null,\n description: commentsEvent ? getEventString(commentsEvent, parsed.useUnicode) : null,\n artist: artistsEvent ? getEventString(artistsEvent, parsed.useUnicode) : null,\n genre: genreEvent ? getEventString(genreEvent, parsed.useUnicode) : null,\n bpm: tempoEvent ? getEventNumber(tempoEvent) / 1000 : null,\n };\n}\n\n/**\n * Writes project metadata to a parsed FLP\n * Only modifies fields that are provided (non-undefined)\n */\nexport function writeProjectMeta(parsed: ParsedFlp, meta: Partial<ProjectMeta>): ParsedFlp {\n return patchEvents(parsed, (event: FlpEvent) => {\n // Patch title\n if (meta.name !== undefined && event.id === EVENT_ID.PROJECT_TITLE) {\n return {\n ...event,\n payload: createTextPayload(meta.name ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch comments/description\n if (meta.description !== undefined && event.id === EVENT_ID.PROJECT_COMMENTS) {\n return {\n ...event,\n payload: createTextPayload(meta.description ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch artists\n if (meta.artist !== undefined && event.id === EVENT_ID.PROJECT_ARTISTS) {\n return {\n ...event,\n payload: createTextPayload(meta.artist ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch genre\n if (meta.genre !== undefined && event.id === EVENT_ID.PROJECT_GENRE) {\n return {\n ...event,\n payload: createTextPayload(meta.genre ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch tempo (stored as BPM * 1000)\n if (meta.bpm && Math.abs(meta.bpm) > 0 && event.id === EVENT_ID.PROJECT_TEMPO) {\n return {\n ...event,\n payload: createNumberPayload(Math.round(Math.abs(meta.bpm) * 1000), \"u32\"),\n };\n }\n\n return event;\n });\n}\n\n// ============================================================================\n// Project Time Info\n// ============================================================================\n\n/**\n * Project creation date and work time\n */\nexport interface ProjectTimeInfo {\n creationDate: Date | null;\n workTimeSeconds: number | null;\n}\n\n// Delphi epoch: December 30, 1899\nconst DELPHI_EPOCH = new Date(1899, 11, 30);\n\n/**\n * Converts Delphi timestamp (days since epoch) to Date\n */\nfunction delphiToDate(days: number): Date {\n const ms = days * 24 * 60 * 60 * 1000;\n return new Date(DELPHI_EPOCH.getTime() + ms);\n}\n\n/**\n * Converts Date to Delphi timestamp (days since epoch)\n */\nfunction dateToDelphiDays(date: Date): number {\n const ms = date.getTime() - DELPHI_EPOCH.getTime();\n return ms / (24 * 60 * 60 * 1000);\n}\n\n/**\n * Reads project time info from a parsed FLP\n * Timestamp event contains: Float64 created_on (days), Float64 time_spent (days)\n */\nexport function readProjectTimeInfo(parsed: ParsedFlp): ProjectTimeInfo {\n const timestampEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TIMESTAMP);\n\n if (!timestampEvent || timestampEvent.payload.length < 16) {\n return {\n creationDate: null,\n workTimeSeconds: null,\n };\n }\n\n const reader = new BinaryReader(timestampEvent.payload);\n const createdOnDays = reader.readF64LE();\n const timeSpentDays = reader.readF64LE();\n\n return {\n creationDate: delphiToDate(createdOnDays),\n workTimeSeconds: timeSpentDays * 24 * 60 * 60, // Convert days to seconds\n };\n}\n\n/**\n * Writes project time info to a parsed FLP\n * Only modifies fields that are provided (non-undefined)\n * \n * If `creationDate` or `workTimeSeconds` is set to `null`, the final value will be set on `0` (Delphi epoch)\n */\nexport function writeProjectTimeInfo(\n parsed: ParsedFlp,\n info: Partial<ProjectTimeInfo>\n): ParsedFlp {\n // Read current values to preserve unmodified fields\n const current = readProjectTimeInfo(parsed);\n\n return patchEvents(parsed, (event: FlpEvent) => {\n if (event.id !== EVENT_ID.PROJECT_TIMESTAMP) {\n return event;\n }\n\n const writer = new BinaryWriter();\n\n // Write creation date\n if (info.creationDate !== undefined && info.creationDate !== null) {\n writer.writeF64LE(dateToDelphiDays(info.creationDate));\n } else if (current.creationDate !== null) {\n writer.writeF64LE(dateToDelphiDays(current.creationDate));\n } else {\n writer.writeF64LE(0);\n }\n\n // Write work time\n if (info.workTimeSeconds !== undefined && info.workTimeSeconds !== null) {\n writer.writeF64LE(info.workTimeSeconds / (24 * 60 * 60)); // Convert seconds to days\n } else if (current.workTimeSeconds !== null) {\n writer.writeF64LE(current.workTimeSeconds / (24 * 60 * 60));\n } else {\n writer.writeF64LE(0);\n }\n\n return {\n ...event,\n payload: writer.toBuffer(),\n };\n });\n}\n\n// ============================================================================\n// Samples\n// ============================================================================\n\n/**\n * Reference to a sample in the project\n */\nexport interface SampleRef {\n /** Index of the event in the events array */\n eventIndex: number;\n /** File path of the sample */\n path: string;\n}\n\n/**\n * Lists all samples in the project\n */\nexport function listSamples(parsed: ParsedFlp): SampleRef[] {\n const samples: SampleRef[] = [];\n\n parsed.events.forEach((event: FlpEvent, index: number) => {\n if (event.id === EVENT_ID.CHANNEL_SAMPLE_PATH) {\n const path = getEventString(event, parsed.useUnicode);\n if (path && path.length > 0) {\n samples.push({\n eventIndex: index,\n path,\n });\n }\n }\n });\n\n return samples;\n}\n\n/**\n * Rewrites sample paths using a mapper function\n */\nexport function rewriteSamplePaths(\n parsed: ParsedFlp,\n mapper: (oldPath: string) => string\n): ParsedFlp {\n return patchEvents(parsed, (event: FlpEvent) => {\n if (event.id !== EVENT_ID.CHANNEL_SAMPLE_PATH) {\n return event;\n }\n\n const oldPath = getEventString(event, parsed.useUnicode);\n if (!oldPath || oldPath.length === 0) {\n return event;\n }\n\n const newPath = mapper(oldPath);\n if (newPath === oldPath) {\n return event;\n }\n\n return {\n ...event,\n payload: createTextPayload(newPath, parsed.useUnicode),\n };\n });\n}\n\n// ============================================================================\n// Plugins (VST)\n// ============================================================================\n\n/**\n * Reference to a plugin in the project\n */\nexport interface PluginRef {\n /** Plugin name (may be null if not available) */\n name: string | null;\n /** Plugin vendor/provider (may be null if not stored in file) */\n vendor: string | null;\n}\n\n/**\n * VST Plugin Event internal IDs (from plugin.py VSTPluginEvent)\n */\nconst VST_EVENT_ID = {\n MIDI: 1,\n FLAGS: 2,\n IO: 30,\n INPUTS: 31,\n OUTPUTS: 32,\n PLUGIN_INFO: 50,\n FOUR_CC: 51,\n GUID: 52,\n STATE: 53,\n NAME: 54,\n PLUGIN_PATH: 55,\n VENDOR: 56,\n} as const;\n\n/**\n * Parses a VST plugin data event to extract name and vendor\n * Based on VSTPluginEvent structure in PyFLP\n */\nfunction parseVstPluginData(payload: Buffer): { name: string | null; vendor: string | null } {\n if (payload.length < 4) {\n return { name: null, vendor: null };\n }\n\n const reader = new BinaryReader(payload);\n\n // First 4 bytes: type marker (8, 10, or 11 for VSTs)\n const typeMarker = reader.readU32LE();\n\n // Skip if not a VST plugin marker\n if (typeMarker !== 8 && typeMarker !== 10 && typeMarker !== 11) {\n return { name: null, vendor: null };\n }\n\n let name: string | null = null;\n let vendor: string | null = null;\n\n // Parse sub-events\n while (reader.remaining() >= 12) {\n // At minimum: 4 (id) + 8 (size)\n const subEventId = reader.readU32LE();\n const dataSize = reader.readU32LE(); // Lower 32 bits of size\n const dataSizeHigh = reader.readU32LE(); // Upper 32 bits (usually 0)\n\n // Combine for 64-bit size (though in practice it's always small)\n const actualSize = dataSize + dataSizeHigh * 0x100000000;\n\n if (actualSize > reader.remaining()) {\n break;\n }\n\n const data = reader.readBytes(actualSize);\n\n // Extract name and vendor\n if (subEventId === VST_EVENT_ID.NAME && actualSize > 0) {\n name = data.toString(\"utf8\").replace(/\\0/g, \"\");\n } else if (subEventId === VST_EVENT_ID.VENDOR && actualSize > 0) {\n vendor = data.toString(\"utf8\").replace(/\\0/g, \"\");\n }\n }\n\n return { name, vendor };\n}\n\n/**\n * Lists all plugins (VST and native) in the project\n * Returns plugin name and vendor where available\n */\nexport function listPlugins(parsed: ParsedFlp): PluginRef[] {\n const plugins: PluginRef[] = [];\n const seenPlugins = new Set<string>();\n\n // Track current channel context\n let currentInternalName: string | null = null;\n let currentPluginName: string | null = null;\n\n for (const event of parsed.events) {\n // Track channel new events as boundaries\n if (event.id === EVENT_ID.CHANNEL_NEW) {\n // Reset context for new channel\n if (currentInternalName || currentPluginName) {\n // Save previous channel's plugin if it wasn't a VST (no plugin data)\n const key = `${currentInternalName ?? \"\"}:${currentPluginName ?? \"\"}`;\n if (!seenPlugins.has(key) && currentInternalName && currentInternalName !== \"Fruity Wrapper\") {\n seenPlugins.add(key);\n plugins.push({\n name: currentPluginName || currentInternalName,\n vendor: null, // Native plugins don't store vendor\n });\n }\n }\n currentInternalName = null;\n currentPluginName = null;\n }\n\n // Track internal name (identifies plugin type)\n if (event.id === EVENT_ID.PLUGIN_INTERNAL_NAME) {\n currentInternalName = getEventString(event, parsed.useUnicode);\n }\n\n // Track display name\n if (event.id === EVENT_ID.PLUGIN_NAME) {\n currentPluginName = getEventString(event, parsed.useUnicode);\n }\n\n // Parse VST plugin data\n if (event.id === EVENT_ID.PLUGIN_DATA && currentInternalName === \"Fruity Wrapper\") {\n const vstData = parseVstPluginData(event.payload);\n if (vstData.name) {\n const key = `vst:${vstData.name}:${vstData.vendor ?? \"\"}`;\n if (!seenPlugins.has(key)) {\n seenPlugins.add(key);\n plugins.push({\n name: vstData.name,\n vendor: vstData.vendor,\n });\n }\n }\n // Reset context after processing VST\n currentInternalName = null;\n currentPluginName = null;\n }\n }\n\n // Handle last channel if any\n if (currentInternalName && currentInternalName !== \"Fruity Wrapper\") {\n const key = `${currentInternalName}:${currentPluginName ?? \"\"}`;\n if (!seenPlugins.has(key)) {\n plugins.push({\n name: currentPluginName || currentInternalName,\n vendor: null,\n });\n }\n }\n\n return plugins;\n}\n\n// ============================================================================\n// FL Version\n// ============================================================================\n\n/**\n * Gets the FL Studio version string from the project\n */\nexport function getFlVersion(parsed: ParsedFlp): string {\n return parsed.flVersion;\n}\n\n/**\n * Gets the PPQ (Pulses Per Quarter note) from the header\n */\nexport function getPPQ(parsed: ParsedFlp): number {\n // PPQ is at offset 12-13 in the header (bytes 12-13 of the 14-byte FLhd chunk)\n const reader = new BinaryReader(parsed.headerChunkBytes);\n reader.seek(12);\n return reader.readU16LE();\n}\n"],"mappings":"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","names":["import_smart_buffer","protobuf","headerSize"]}
1
+ {"version":3,"sources":["../index.ts","../src/generated/events.generated.ts","../src/io/BinaryReader.ts","../src/io/BinaryWriter.ts","../src/parser/FlpParser.ts","../src/api/ProjectApi.ts"],"sourcesContent":["/**\n * ts-flp - A minimal TypeScript port of PyFLP for FL Studio .flp files\n *\n * This library provides conservative (non-destructive) parsing and patching\n * of FL Studio project files. It preserves all unknown data byte-for-byte.\n *\n * Features:\n * - Read and modify project metadata (name, description, artist, BPM)\n * - Read and modify timestamps (creation date, work time)\n * - List and remap sample paths\n * - List VST plugins (name and vendor)\n * - Conservative round-trip: unchanged files are byte-identical\n *\n * @example\n * ```typescript\n * import { parseFlp, serializeFlp, readProjectMeta, writeProjectMeta } from \"ts-flp\";\n * import * as fs from \"fs\";\n *\n * // Parse an FLP file\n * const buffer = fs.readFileSync(\"project.flp\");\n * const parsed = parseFlp(buffer);\n *\n * // Read metadata\n * const meta = readProjectMeta(parsed);\n * console.log(meta.name, meta.bpm);\n *\n * // Modify and save\n * const modified = writeProjectMeta(parsed, { bpm: 140 });\n * fs.writeFileSync(\"project_modified.flp\", serializeFlp(modified));\n * ```\n */\n\n// Re-export parser functions\nexport {\n createEvent, createNumberPayload, createTextPayload, findEvents,\n findFirstEvent, getEventNumber, getEventString, parseFlp, patchEvents, serializeFlp, type FlpEvent, type ParsedFlp\n} from \"./src/parser/FlpParser.js\";\n\n// Re-export API functions\nexport {\n getFlVersion,\n getPPQ,\n listPlugins,\n listSamples,\n readProjectMeta,\n readProjectTimeInfo,\n rewriteSamplePaths,\n writeProjectMeta,\n writeProjectTimeInfo,\n type PluginRef,\n type ProjectMeta,\n type ProjectTimeInfo,\n type SampleRef\n} from \"./src/api/ProjectApi.js\";\n\n// Re-export event constants\nexport { EVENT_ID, EVENT_KIND, getEventFixedSize, getEventKind } from \"./src/generated/events.generated.js\";\n\n// Re-export IO utilities for advanced usage\nexport { BinaryReader } from \"./src/io/BinaryReader.js\";\nexport { BinaryWriter, encodeVarInt, varIntSize } from \"./src/io/BinaryWriter.js\";\n\n","/**\r\n * Auto-generated event constants from PyFLP\r\n * FL Studio .flp file event IDs and types\r\n *\r\n * Event ID ranges:\r\n * - BYTE (0-63): 1 byte data\r\n * - WORD (64-127): 2 bytes data\r\n * - DWORD (128-191): 4 bytes data\r\n * - TEXT (192-207): Variable length strings (+ some IDs in DATA range)\r\n * - DATA (208+): Variable length binary data\r\n */\r\n\r\n// Base offsets for event ID ranges\r\nexport const BYTE = 0;\r\nexport const WORD = 64;\r\nexport const DWORD = 128;\r\nexport const TEXT = 192;\r\nexport const DATA = 208;\r\n\r\n// IDs for TEXT events that use UTF-16LE in newer FL versions\r\nexport const NEW_TEXT_IDS = [\r\n TEXT + 49, // ArrangementID.Name\r\n TEXT + 39, // DisplayGroupID.Name\r\n TEXT + 47, // TrackID.Name\r\n] as const;\r\n\r\n/**\r\n * Event IDs used by FL Studio project files\r\n * Based on PyFLP's event definitions\r\n */\r\nexport const EVENT_ID = {\r\n // === Project IDs (from project.py) ===\r\n PROJECT_LOOP_ACTIVE: 9,\r\n PROJECT_SHOW_INFO: 10,\r\n PROJECT__VOLUME: 12,\r\n PROJECT_PAN_LAW: 23,\r\n PROJECT_LICENSED: 28,\r\n PROJECT__TEMPO_COARSE: WORD + 2, // 66\r\n PROJECT_PITCH: WORD + 16, // 80\r\n PROJECT__TEMPO_FINE: WORD + 29, // 93\r\n PROJECT_CUR_GROUP_ID: DWORD + 18,\r\n PROJECT_TEMPO: DWORD + 28,\r\n PROJECT_FL_BUILD: DWORD + 31,\r\n PROJECT_TITLE: TEXT + 2, // 194\r\n PROJECT_COMMENTS: TEXT + 3, // 195\r\n PROJECT_URL: TEXT + 5, // 197\r\n PROJECT__RTF_COMMENTS: TEXT + 6, // 198\r\n PROJECT_FL_VERSION: TEXT + 7, // 199\r\n PROJECT_LICENSEE: TEXT + 8, // 200\r\n PROJECT_DATA_PATH: TEXT + 10, // 202\r\n PROJECT_GENRE: TEXT + 14, // 206\r\n PROJECT_ARTISTS: TEXT + 15, // 207\r\n PROJECT_TIMESTAMP: DATA + 29, // 237\r\n\r\n // === Channel IDs (from channel.py) ===\r\n CHANNEL_IS_ENABLED: 0,\r\n CHANNEL__VOL_BYTE: 2,\r\n CHANNEL__PAN_BYTE: 3,\r\n CHANNEL_ZIPPED: 15,\r\n CHANNEL_PING_PONG_LOOP: 20,\r\n CHANNEL_TYPE: 21,\r\n CHANNEL_ROUTED_TO: 22,\r\n CHANNEL_IS_LOCKED: 32,\r\n CHANNEL_NEW: WORD,\r\n CHANNEL_FREQ_TILT: WORD + 5, // 69\r\n CHANNEL_FX_FLAGS: WORD + 6, // 70\r\n CHANNEL_CUTOFF: WORD + 7, // 71\r\n CHANNEL__VOL_WORD: WORD + 8, // 72\r\n CHANNEL__PAN_WORD: WORD + 9, // 73\r\n CHANNEL_PREAMP: WORD + 10, // 74\r\n CHANNEL_FADE_OUT: WORD + 11, // 75\r\n CHANNEL_FADE_IN: WORD + 12, // 76\r\n CHANNEL_RESONANCE: WORD + 19, // 83\r\n CHANNEL_STEREO_DELAY: WORD + 21, // 85\r\n CHANNEL_POGO: WORD + 22, // 86\r\n CHANNEL_TIME_SHIFT: WORD + 25, // 89\r\n CHANNEL_CHILDREN: WORD + 30, // 94\r\n CHANNEL_SWING: WORD + 33, // 97\r\n CHANNEL_RING_MOD: DWORD + 3,\r\n CHANNEL_CUT_GROUP: DWORD + 4,\r\n CHANNEL_ROOT_NOTE: DWORD + 7,\r\n CHANNEL_DELAY_MOD_XY: DWORD + 10,\r\n CHANNEL_REVERB: DWORD + 11,\r\n CHANNEL__STRETCH_TIME: DWORD + 12,\r\n CHANNEL_FINE_TUNE: DWORD + 14,\r\n CHANNEL_SAMPLER_FLAGS: DWORD + 15,\r\n CHANNEL_LAYER_FLAGS: DWORD + 16,\r\n CHANNEL_GROUP_NUM: DWORD + 17,\r\n CHANNEL_AU_SAMPLE_RATE: DWORD + 25,\r\n CHANNEL__NAME: TEXT,\r\n CHANNEL_SAMPLE_PATH: TEXT + 4, // 196\r\n CHANNEL_DELAY: DATA + 1, // 209\r\n CHANNEL_PARAMETERS: DATA + 7, // 215\r\n CHANNEL_ENVELOPE_LFO: DATA + 10, // 218\r\n CHANNEL_LEVELS: DATA + 11, // 219\r\n CHANNEL_POLYPHONY: DATA + 13, // 221\r\n CHANNEL_TRACKING: DATA + 20, // 228\r\n CHANNEL_LEVEL_ADJUSTS: DATA + 21, // 229\r\n CHANNEL_AUTOMATION: DATA + 26, // 234\r\n\r\n // === Plugin IDs (from plugin.py) ===\r\n PLUGIN_COLOR: DWORD,\r\n PLUGIN_ICON: DWORD + 27,\r\n PLUGIN_INTERNAL_NAME: TEXT + 9, // 201\r\n PLUGIN_NAME: TEXT + 11, // 203\r\n PLUGIN_WRAPPER: DATA + 4, // 212\r\n PLUGIN_DATA: DATA + 5, // 213\r\n\r\n // === Display Group IDs ===\r\n DISPLAY_GROUP_NAME: TEXT + 39, // 231\r\n\r\n // === Rack IDs ===\r\n RACK_SWING: 11,\r\n RACK__FIT_TO_STEPS: 13,\r\n RACK_WINDOW_HEIGHT: DWORD + 5,\r\n\r\n // === Mixer IDs (from mixer.py) ===\r\n MIXER_SLOT_INDEX: WORD + 34, // 98\r\n MIXER_INSERT_ICON: WORD + 31, // 95\r\n MIXER_INSERT_OUTPUT: DWORD + 19,\r\n MIXER_INSERT_COLOR: DWORD + 21,\r\n MIXER_INSERT_INPUT: DWORD + 26,\r\n MIXER_INSERT_NAME: TEXT + 12, // 204\r\n MIXER_INSERT_ROUTING: DATA + 27, // 235\r\n MIXER_INSERT_FLAGS: DATA + 28, // 236\r\n} as const;\r\n\r\n/**\r\n * Event data type classification\r\n */\r\nexport type EventKind =\r\n | \"u8\"\r\n | \"i8\"\r\n | \"u16\"\r\n | \"i16\"\r\n | \"u32\"\r\n | \"i32\"\r\n | \"f32\"\r\n | \"text\"\r\n | \"data\"\r\n | \"unknown\";\r\n\r\n/**\r\n * Maps event IDs to their data type\r\n * Used for proper serialization/deserialization\r\n */\r\nexport const EVENT_KIND: Record<number, EventKind> = {\r\n // Project events\r\n [EVENT_ID.PROJECT_LOOP_ACTIVE]: \"u8\",\r\n [EVENT_ID.PROJECT_SHOW_INFO]: \"u8\",\r\n [EVENT_ID.PROJECT__VOLUME]: \"u8\",\r\n [EVENT_ID.PROJECT_PAN_LAW]: \"u8\",\r\n [EVENT_ID.PROJECT_LICENSED]: \"u8\",\r\n [EVENT_ID.PROJECT__TEMPO_COARSE]: \"u16\",\r\n [EVENT_ID.PROJECT_PITCH]: \"i16\",\r\n [EVENT_ID.PROJECT__TEMPO_FINE]: \"u16\",\r\n [EVENT_ID.PROJECT_CUR_GROUP_ID]: \"i32\",\r\n [EVENT_ID.PROJECT_TEMPO]: \"u32\",\r\n [EVENT_ID.PROJECT_FL_BUILD]: \"u32\",\r\n [EVENT_ID.PROJECT_TITLE]: \"text\",\r\n [EVENT_ID.PROJECT_COMMENTS]: \"text\",\r\n [EVENT_ID.PROJECT_URL]: \"text\",\r\n [EVENT_ID.PROJECT__RTF_COMMENTS]: \"text\",\r\n [EVENT_ID.PROJECT_FL_VERSION]: \"text\",\r\n [EVENT_ID.PROJECT_LICENSEE]: \"text\",\r\n [EVENT_ID.PROJECT_DATA_PATH]: \"text\",\r\n [EVENT_ID.PROJECT_GENRE]: \"text\",\r\n [EVENT_ID.PROJECT_ARTISTS]: \"text\",\r\n [EVENT_ID.PROJECT_TIMESTAMP]: \"data\",\r\n\r\n // Channel events\r\n [EVENT_ID.CHANNEL_IS_ENABLED]: \"u8\",\r\n [EVENT_ID.CHANNEL__VOL_BYTE]: \"u8\",\r\n [EVENT_ID.CHANNEL__PAN_BYTE]: \"u8\",\r\n [EVENT_ID.CHANNEL_ZIPPED]: \"u8\",\r\n [EVENT_ID.CHANNEL_PING_PONG_LOOP]: \"u8\",\r\n [EVENT_ID.CHANNEL_TYPE]: \"u8\",\r\n [EVENT_ID.CHANNEL_ROUTED_TO]: \"i8\",\r\n [EVENT_ID.CHANNEL_IS_LOCKED]: \"u8\",\r\n [EVENT_ID.CHANNEL_NEW]: \"u16\",\r\n [EVENT_ID.CHANNEL_FREQ_TILT]: \"u16\",\r\n [EVENT_ID.CHANNEL_FX_FLAGS]: \"u16\",\r\n [EVENT_ID.CHANNEL_CUTOFF]: \"u16\",\r\n [EVENT_ID.CHANNEL__VOL_WORD]: \"u16\",\r\n [EVENT_ID.CHANNEL__PAN_WORD]: \"u16\",\r\n [EVENT_ID.CHANNEL_PREAMP]: \"u16\",\r\n [EVENT_ID.CHANNEL_FADE_OUT]: \"u16\",\r\n [EVENT_ID.CHANNEL_FADE_IN]: \"u16\",\r\n [EVENT_ID.CHANNEL_RESONANCE]: \"u16\",\r\n [EVENT_ID.CHANNEL_STEREO_DELAY]: \"u16\",\r\n [EVENT_ID.CHANNEL_POGO]: \"u16\",\r\n [EVENT_ID.CHANNEL_TIME_SHIFT]: \"u16\",\r\n [EVENT_ID.CHANNEL_CHILDREN]: \"u16\",\r\n [EVENT_ID.CHANNEL_SWING]: \"u16\",\r\n [EVENT_ID.CHANNEL_RING_MOD]: \"u32\",\r\n [EVENT_ID.CHANNEL_CUT_GROUP]: \"u32\",\r\n [EVENT_ID.CHANNEL_ROOT_NOTE]: \"u32\",\r\n [EVENT_ID.CHANNEL_DELAY_MOD_XY]: \"u32\",\r\n [EVENT_ID.CHANNEL_REVERB]: \"u32\",\r\n [EVENT_ID.CHANNEL__STRETCH_TIME]: \"f32\",\r\n [EVENT_ID.CHANNEL_FINE_TUNE]: \"i32\",\r\n [EVENT_ID.CHANNEL_SAMPLER_FLAGS]: \"u32\",\r\n [EVENT_ID.CHANNEL_LAYER_FLAGS]: \"u32\",\r\n [EVENT_ID.CHANNEL_GROUP_NUM]: \"i32\",\r\n [EVENT_ID.CHANNEL_AU_SAMPLE_RATE]: \"u32\",\r\n [EVENT_ID.CHANNEL__NAME]: \"text\",\r\n [EVENT_ID.CHANNEL_SAMPLE_PATH]: \"text\",\r\n [EVENT_ID.CHANNEL_DELAY]: \"data\",\r\n [EVENT_ID.CHANNEL_PARAMETERS]: \"data\",\r\n [EVENT_ID.CHANNEL_ENVELOPE_LFO]: \"data\",\r\n [EVENT_ID.CHANNEL_LEVELS]: \"data\",\r\n [EVENT_ID.CHANNEL_POLYPHONY]: \"data\",\r\n [EVENT_ID.CHANNEL_TRACKING]: \"data\",\r\n [EVENT_ID.CHANNEL_LEVEL_ADJUSTS]: \"data\",\r\n [EVENT_ID.CHANNEL_AUTOMATION]: \"data\",\r\n\r\n // Plugin events\r\n [EVENT_ID.PLUGIN_COLOR]: \"u32\",\r\n [EVENT_ID.PLUGIN_ICON]: \"u32\",\r\n [EVENT_ID.PLUGIN_INTERNAL_NAME]: \"text\",\r\n [EVENT_ID.PLUGIN_NAME]: \"text\",\r\n [EVENT_ID.PLUGIN_WRAPPER]: \"data\",\r\n [EVENT_ID.PLUGIN_DATA]: \"data\",\r\n\r\n // Display Group events\r\n [EVENT_ID.DISPLAY_GROUP_NAME]: \"data\",\r\n\r\n // Rack events\r\n [EVENT_ID.RACK_SWING]: \"u8\",\r\n [EVENT_ID.RACK__FIT_TO_STEPS]: \"u8\",\r\n [EVENT_ID.RACK_WINDOW_HEIGHT]: \"u32\",\r\n\r\n // Mixer events\r\n [EVENT_ID.MIXER_SLOT_INDEX]: \"u16\",\r\n [EVENT_ID.MIXER_INSERT_ICON]: \"i16\",\r\n [EVENT_ID.MIXER_INSERT_OUTPUT]: \"i32\",\r\n [EVENT_ID.MIXER_INSERT_COLOR]: \"u32\",\r\n [EVENT_ID.MIXER_INSERT_INPUT]: \"i32\",\r\n [EVENT_ID.MIXER_INSERT_NAME]: \"text\",\r\n [EVENT_ID.MIXER_INSERT_ROUTING]: \"data\",\r\n [EVENT_ID.MIXER_INSERT_FLAGS]: \"data\",\r\n\r\n};\r\n\r\n/**\r\n * Determines the event kind based on ID\r\n * Falls back to range-based detection if not explicitly mapped\r\n */\r\nexport function getEventKind(eventId: number): EventKind {\r\n // Check explicit mapping first\r\n const mapped = EVENT_KIND[eventId];\r\n if (mapped !== undefined) {\r\n return mapped;\r\n }\r\n\r\n // Fall back to range-based detection\r\n if (eventId < WORD) {\r\n return \"u8\";\r\n } else if (eventId < DWORD) {\r\n return \"u16\";\r\n } else if (eventId < TEXT) {\r\n return \"u32\";\r\n } else if (eventId < DATA || NEW_TEXT_IDS.includes(eventId as 241 | 231 | 239)) {\r\n return \"text\";\r\n } else {\r\n return \"data\";\r\n }\r\n}\r\n\r\n/**\r\n * Returns the fixed size for BYTE/WORD/DWORD events\r\n * Returns -1 for variable-length events (TEXT/DATA)\r\n */\r\nexport function getEventFixedSize(eventId: number): number {\r\n if (eventId < WORD) {\r\n return 1;\r\n } else if (eventId < DWORD) {\r\n return 2;\r\n } else if (eventId < TEXT) {\r\n return 4;\r\n }\r\n return -1; // Variable length\r\n}\r\n","/**\n * Binary reader for FL Studio .flp files\n * Uses smart-buffer for binary reading and protobufjs for VarInt decoding\n */\n\nimport protobuf from \"protobufjs/minimal.js\";\nimport { SmartBuffer } from \"smart-buffer\";\n\nexport class BinaryReader {\n private sb: SmartBuffer;\n private source: Buffer;\n\n constructor(buffer: Buffer) {\n this.source = buffer;\n this.sb = SmartBuffer.fromBuffer(buffer);\n }\n\n /**\n * Returns current read position\n */\n tell(): number {\n return this.sb.readOffset;\n }\n\n /**\n * Seeks to an absolute position\n */\n seek(pos: number): void {\n this.sb.readOffset = pos;\n }\n\n /**\n * Seeks relative to current position\n */\n skip(offset: number): void {\n this.sb.readOffset += offset;\n }\n\n /**\n * Returns total buffer length\n */\n length(): number {\n return this.sb.length;\n }\n\n /**\n * Returns remaining bytes to read\n */\n remaining(): number {\n return this.sb.remaining();\n }\n\n /**\n * Returns true if there are more bytes to read\n */\n hasMore(): boolean {\n return this.sb.remaining() > 0;\n }\n\n /**\n * Reads an unsigned 8-bit integer\n */\n readU8(): number {\n return this.sb.readUInt8();\n }\n\n /**\n * Reads a signed 8-bit integer\n */\n readI8(): number {\n return this.sb.readInt8();\n }\n\n /**\n * Reads an unsigned 16-bit integer (little-endian)\n */\n readU16LE(): number {\n return this.sb.readUInt16LE();\n }\n\n /**\n * Reads a signed 16-bit integer (little-endian)\n */\n readI16LE(): number {\n return this.sb.readInt16LE();\n }\n\n /**\n * Reads an unsigned 32-bit integer (little-endian)\n */\n readU32LE(): number {\n return this.sb.readUInt32LE();\n }\n\n /**\n * Reads a signed 32-bit integer (little-endian)\n */\n readI32LE(): number {\n return this.sb.readInt32LE();\n }\n\n /**\n * Reads a 32-bit float (little-endian)\n */\n readF32LE(): number {\n return this.sb.readFloatLE();\n }\n\n /**\n * Reads a 64-bit float (little-endian)\n */\n readF64LE(): number {\n return this.sb.readDoubleLE();\n }\n\n /**\n * Reads raw bytes\n */\n readBytes(length: number): Buffer {\n return this.sb.readBuffer(length);\n }\n\n /**\n * Reads a VarInt (variable-length integer) using protobufjs\n * FL Studio uses this encoding for TEXT and DATA event sizes\n */\n readVarInt(): number {\n // Get remaining buffer from current position\n const remainingBuffer = this.source.subarray(this.sb.readOffset);\n const reader = protobuf.Reader.create(remainingBuffer);\n const value = reader.uint32();\n // Advance our position by how many bytes protobufjs consumed\n this.sb.readOffset += reader.pos;\n return value;\n }\n\n /**\n * Reads an ASCII string (null-terminated)\n */\n readAsciiString(length: number): string {\n const str = this.sb.readString(length, \"ascii\");\n // Remove null terminator if present\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n\n /**\n * Reads a UTF-16LE string (null-terminated)\n */\n readUtf16LEString(length: number): string {\n const bytes = this.sb.readBuffer(length);\n const str = bytes.toString(\"utf16le\");\n // Remove null terminator if present\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n\n /**\n * Peeks at bytes without advancing position\n */\n peek(length: number): Buffer {\n const pos = this.sb.readOffset;\n const data = this.sb.readBuffer(length);\n this.sb.readOffset = pos;\n return data;\n }\n\n /**\n * Peeks at a single byte without advancing position\n */\n peekU8(): number {\n const pos = this.sb.readOffset;\n const value = this.sb.readUInt8();\n this.sb.readOffset = pos;\n return value;\n }\n\n /**\n * Returns the internal buffer\n */\n toBuffer(): Buffer {\n return this.source;\n }\n}\n","/**\n * Binary writer for FL Studio .flp files\n * Uses smart-buffer for binary writing and protobufjs for VarInt encoding\n */\n\nimport { SmartBuffer } from \"smart-buffer\";\nimport protobuf from \"protobufjs/minimal.js\";\n\nexport class BinaryWriter {\n private sb: SmartBuffer;\n\n constructor() {\n this.sb = new SmartBuffer();\n }\n\n /**\n * Returns current write position (total bytes written)\n */\n tell(): number {\n return this.sb.writeOffset;\n }\n\n /**\n * Writes an unsigned 8-bit integer\n */\n writeU8(value: number): void {\n this.sb.writeUInt8(value & 0xff);\n }\n\n /**\n * Writes a signed 8-bit integer\n */\n writeI8(value: number): void {\n this.sb.writeInt8(value);\n }\n\n /**\n * Writes an unsigned 16-bit integer (little-endian)\n */\n writeU16LE(value: number): void {\n this.sb.writeUInt16LE(value & 0xffff);\n }\n\n /**\n * Writes a signed 16-bit integer (little-endian)\n */\n writeI16LE(value: number): void {\n this.sb.writeInt16LE(value);\n }\n\n /**\n * Writes an unsigned 32-bit integer (little-endian)\n */\n writeU32LE(value: number): void {\n this.sb.writeUInt32LE(value >>> 0);\n }\n\n /**\n * Writes a signed 32-bit integer (little-endian)\n */\n writeI32LE(value: number): void {\n this.sb.writeInt32LE(value);\n }\n\n /**\n * Writes a 32-bit float (little-endian)\n */\n writeF32LE(value: number): void {\n this.sb.writeFloatLE(value);\n }\n\n /**\n * Writes a 64-bit float (little-endian)\n */\n writeF64LE(value: number): void {\n this.sb.writeDoubleLE(value);\n }\n\n /**\n * Writes raw bytes\n */\n writeBytes(data: Buffer): void {\n this.sb.writeBuffer(data);\n }\n\n /**\n * Writes a VarInt (variable-length integer) using protobufjs\n * FL Studio uses this encoding for TEXT and DATA event sizes\n */\n writeVarInt(value: number): void {\n const writer = protobuf.Writer.create();\n writer.uint32(value);\n const encoded = writer.finish();\n this.sb.writeBuffer(Buffer.from(encoded));\n }\n\n /**\n * Writes an ASCII string (null-terminated)\n */\n writeAsciiString(str: string): void {\n this.sb.writeString(str + \"\\0\", \"ascii\");\n }\n\n /**\n * Writes a UTF-16LE string (null-terminated)\n */\n writeUtf16LEString(str: string): void {\n const buf = Buffer.from(str + \"\\0\", \"utf16le\");\n this.sb.writeBuffer(buf);\n }\n\n /**\n * Gets the final buffer\n */\n toBuffer(): Buffer {\n return this.sb.toBuffer();\n }\n}\n\n/**\n * Encodes a VarInt to a Buffer using protobufjs (standalone utility)\n */\nexport function encodeVarInt(value: number): Buffer {\n const writer = protobuf.Writer.create();\n writer.uint32(value);\n return Buffer.from(writer.finish());\n}\n\n/**\n * Calculates the byte size needed for a VarInt\n */\nexport function varIntSize(value: number): number {\n if (value < 0x80) return 1;\n if (value < 0x4000) return 2;\n if (value < 0x200000) return 3;\n if (value < 0x10000000) return 4;\n return 5;\n}\n","/**\n * FL Studio .flp file parser and serializer\n * Conservative patch approach: preserves all unknown data byte-for-byte\n */\n\nimport { DWORD, EVENT_ID, TEXT, WORD, getEventKind } from \"../generated/events.generated.js\";\nimport { BinaryReader } from \"../io/BinaryReader.js\";\nimport { BinaryWriter } from \"../io/BinaryWriter.js\";\n\n/**\n * Represents a single event in an FLP file\n * Stores raw bytes to enable conservative patching\n */\nexport interface FlpEvent {\n /** Event ID (0-255) */\n id: number;\n /** Data type classification */\n kind: \"u8\" | \"i8\" | \"u16\" | \"i16\" | \"u32\" | \"i32\" | \"f32\" | \"text\" | \"data\" | \"unknown\";\n /** Original header bytes (ID + size for variable-length events) */\n header: Buffer;\n /** Event payload data */\n payload: Buffer;\n}\n\n/**\n * Represents a parsed FLP file\n * Contains all data needed for conservative round-trip\n */\nexport interface ParsedFlp {\n /** FLhd chunk (header) - 14 bytes total */\n headerChunkBytes: Buffer;\n /** FLdt chunk header (magic + size placeholder) - 8 bytes */\n fldtHeaderBytes: Buffer;\n /** All events from the FLdt chunk */\n events: FlpEvent[];\n /** Any trailing bytes after events (should be empty) */\n trailingBytes: Buffer | undefined;\n /** Detected FL version for string encoding */\n flVersion: string;\n /** Whether to use UTF-16LE for strings (FL >= 11.5) */\n useUnicode: boolean;\n}\n\n/**\n * Header structure constants\n */\nconst FLP_HEADER_MAGIC = \"FLhd\";\nconst FLP_DATA_MAGIC = \"FLdt\";\nconst FLP_HEADER_SIZE = 6; // Expected header chunk size\n\n/**\n * Parses an FL Studio project file\n *\n * @param buffer - Raw file data\n * @returns Parsed FLP structure\n * @throws Error if file is corrupted or invalid\n */\nexport function parseFlp(buffer: Buffer): ParsedFlp {\n const reader = new BinaryReader(buffer);\n\n // Read FLhd header chunk\n const headerMagic = reader.readBytes(4).toString(\"ascii\");\n if (headerMagic !== FLP_HEADER_MAGIC) {\n throw new Error(`Invalid header magic: expected 'FLhd', got '${headerMagic}'`);\n }\n\n const headerSize = reader.readU32LE();\n if (headerSize !== FLP_HEADER_SIZE) {\n throw new Error(`Invalid header size: expected ${FLP_HEADER_SIZE}, got ${headerSize}`);\n }\n\n const format = reader.readI16LE();\n const channelCount = reader.readU16LE();\n const ppq = reader.readU16LE();\n\n // Validate format (0 = normal project)\n if (format < -1 || format > 0x50) {\n throw new Error(`Invalid file format: ${format}`);\n }\n\n // Store complete header chunk (14 bytes)\n const headerChunkBytes = buffer.subarray(0, 14);\n\n // Read FLdt data chunk header\n const dataMagic = reader.readBytes(4).toString(\"ascii\");\n if (dataMagic !== FLP_DATA_MAGIC) {\n throw new Error(`Invalid data magic: expected 'FLdt', got '${dataMagic}'`);\n }\n\n const eventsSize = reader.readU32LE();\n\n // Validate events size\n const expectedFileSize = 22 + eventsSize; // 14 (FLhd) + 8 (FLdt header) + events\n if (buffer.length !== expectedFileSize) {\n throw new Error(\n `Data chunk size mismatch: expected file size ${expectedFileSize}, got ${buffer.length}`\n );\n }\n\n // Store FLdt header (8 bytes)\n const fldtHeaderBytes = buffer.subarray(14, 22);\n\n // Parse events\n const events: FlpEvent[] = [];\n let flVersion = \"0.0.0\";\n let useUnicode = false;\n let detectedVersion = false;\n\n const eventsStart = reader.tell();\n const eventsEnd = eventsStart + eventsSize;\n\n while (reader.tell() < eventsEnd) {\n const eventStart = reader.tell();\n const eventId = reader.readU8();\n\n let payloadSize: number;\n let headerSize: number;\n\n // Determine payload size based on event ID range\n if (eventId < WORD) {\n // BYTE range: 1 byte payload\n payloadSize = 1;\n headerSize = 1; // Just the ID\n } else if (eventId < DWORD) {\n // WORD range: 2 bytes payload\n payloadSize = 2;\n headerSize = 1;\n } else if (eventId < TEXT) {\n // DWORD range: 4 bytes payload\n payloadSize = 4;\n headerSize = 1;\n } else {\n // TEXT/DATA range: VarInt-encoded size\n const sizeStart = reader.tell();\n payloadSize = reader.readVarInt();\n headerSize = 1 + (reader.tell() - sizeStart); // ID + VarInt bytes\n }\n\n // Read payload\n const payload = reader.readBytes(payloadSize);\n\n // Extract header from original buffer for byte-perfect round-trip\n const header = buffer.subarray(eventStart, eventStart + headerSize);\n\n // Determine event kind\n const kind = getEventKind(eventId);\n\n const event: FlpEvent = {\n id: eventId,\n kind,\n header: Buffer.from(header),\n payload: Buffer.from(payload),\n };\n\n events.push(event);\n\n // Detect FL version from FLVersion event\n if (eventId === EVENT_ID.PROJECT_FL_VERSION && !detectedVersion) {\n const candidate = payload.toString(\"ascii\").replace(/\\0/g, \"\").trim();\n const isSemverLike = /^\\d+(\\.\\d+)+$/.test(candidate);\n if (isSemverLike) {\n flVersion = candidate;\n const parts = flVersion.split(\".\").map((p) => parseInt(p, 10));\n const major = parts[0] ?? 0;\n const minor = parts[1] ?? 0;\n useUnicode = parts.length >= 2 && (major > 11 || (major === 11 && minor >= 5));\n detectedVersion = true;\n }\n }\n }\n\n // Check for trailing bytes\n let trailingBytes: Buffer | undefined;\n if (reader.remaining() > 0) {\n trailingBytes = reader.readBytes(reader.remaining());\n }\n\n return {\n headerChunkBytes: Buffer.from(headerChunkBytes),\n fldtHeaderBytes: Buffer.from(fldtHeaderBytes),\n events,\n trailingBytes,\n flVersion,\n useUnicode,\n };\n}\n\n/**\n * Serializes a parsed FLP back to a buffer\n * Recalculates FLdt chunk size if events have changed\n *\n * @param parsed - Parsed FLP structure\n * @returns Serialized buffer\n */\nexport function serializeFlp(parsed: ParsedFlp): Buffer {\n const writer = new BinaryWriter();\n\n // Write FLhd header chunk (preserved byte-for-byte)\n writer.writeBytes(parsed.headerChunkBytes);\n\n // Calculate total events size\n let eventsSize = 0;\n for (const event of parsed.events) {\n eventsSize += serializeEvent(event).length;\n }\n\n // Add trailing bytes if present\n if (parsed.trailingBytes) {\n eventsSize += parsed.trailingBytes.length;\n }\n\n // Write FLdt header with updated size\n writer.writeBytes(Buffer.from(FLP_DATA_MAGIC, \"ascii\"));\n writer.writeU32LE(eventsSize);\n\n // Write all events\n for (const event of parsed.events) {\n writer.writeBytes(serializeEvent(event));\n }\n\n // Write trailing bytes if present\n if (parsed.trailingBytes) {\n writer.writeBytes(parsed.trailingBytes);\n }\n\n return writer.toBuffer();\n}\n\n/**\n * Serializes a single event to bytes\n * Uses original header for unchanged events, recalculates for modified ones\n */\nfunction serializeEvent(event: FlpEvent): Buffer {\n const writer = new BinaryWriter();\n\n // Conservative mode: preserve the original header bytes whenever available.\n // This keeps unchanged files byte-identical, including exact VarInt encoding.\n if (event.header.length > 0) {\n writer.writeBytes(event.header);\n } else {\n writer.writeU8(event.id);\n if (event.id >= TEXT) {\n writer.writeVarInt(event.payload.length);\n }\n }\n\n // Write payload\n writer.writeBytes(event.payload);\n\n return writer.toBuffer();\n}\n\n/**\n * Creates a new event with the given ID and payload\n */\nexport function createEvent(id: number, payload: Buffer): FlpEvent {\n const kind = getEventKind(id);\n\n // Build header\n const headerWriter = new BinaryWriter();\n headerWriter.writeU8(id);\n\n if (id >= TEXT) {\n headerWriter.writeVarInt(payload.length);\n }\n\n return {\n id,\n kind,\n header: headerWriter.toBuffer(),\n payload: Buffer.from(payload),\n };\n}\n\n/**\n * Applies a patcher function to all events\n * Returns a new ParsedFlp with patched events\n *\n * @param parsed - Original parsed FLP\n * @param patcher - Function that transforms each event\n * @returns New ParsedFlp with patched events\n */\nexport function patchEvents(\n parsed: ParsedFlp,\n patcher: (event: FlpEvent, index: number) => FlpEvent\n): ParsedFlp {\n const patchedEvents = parsed.events.map((event, index) => {\n const patched = patcher(event, index);\n\n // If payload changed, recalculate header for TEXT/DATA events\n if (patched.payload !== event.payload && patched.id >= TEXT) {\n const headerWriter = new BinaryWriter();\n headerWriter.writeU8(patched.id);\n headerWriter.writeVarInt(patched.payload.length);\n return {\n ...patched,\n header: headerWriter.toBuffer(),\n };\n }\n\n return patched;\n });\n\n return {\n ...parsed,\n events: patchedEvents,\n };\n}\n\n/**\n * Finds all events with a specific ID\n */\nexport function findEvents(parsed: ParsedFlp, eventId: number): FlpEvent[] {\n return parsed.events.filter((e) => e.id === eventId);\n}\n\n/**\n * Finds the first event with a specific ID\n */\nexport function findFirstEvent(parsed: ParsedFlp, eventId: number): FlpEvent | undefined {\n return parsed.events.find((e) => e.id === eventId);\n}\n\n/**\n * Gets event payload as a string (handles encoding)\n */\nexport function getEventString(event: FlpEvent, useUnicode: boolean): string {\n if (event.kind !== \"text\") {\n throw new Error(`Event ${event.id} is not a text event`);\n }\n\n if (useUnicode) {\n const str = event.payload.toString(\"utf16le\");\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n } else {\n const str = event.payload.toString(\"ascii\");\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n}\n\n/**\n * Creates a text payload with proper encoding\n */\nexport function createTextPayload(text: string, useUnicode: boolean): Buffer {\n if (useUnicode) {\n return Buffer.from(text + \"\\0\", \"utf16le\");\n } else {\n return Buffer.from(text + \"\\0\", \"ascii\");\n }\n}\n\n/**\n * Gets event payload as a number\n */\nexport function getEventNumber(event: FlpEvent): number {\n const reader = new BinaryReader(event.payload);\n\n switch (event.kind) {\n case \"u8\":\n return reader.readU8();\n case \"i8\":\n return reader.readI8();\n case \"u16\":\n return reader.readU16LE();\n case \"i16\":\n return reader.readI16LE();\n case \"u32\":\n return reader.readU32LE();\n case \"i32\":\n return reader.readI32LE();\n case \"f32\":\n return reader.readF32LE();\n default:\n throw new Error(`Cannot get number from ${event.kind} event`);\n }\n}\n\n/**\n * Creates a numeric payload\n */\nexport function createNumberPayload(\n value: number,\n kind: \"u8\" | \"i8\" | \"u16\" | \"i16\" | \"u32\" | \"i32\" | \"f32\"\n): Buffer {\n const writer = new BinaryWriter();\n\n switch (kind) {\n case \"u8\":\n writer.writeU8(value);\n break;\n case \"i8\":\n writer.writeI8(value);\n break;\n case \"u16\":\n writer.writeU16LE(value);\n break;\n case \"i16\":\n writer.writeI16LE(value);\n break;\n case \"u32\":\n writer.writeU32LE(value);\n break;\n case \"i32\":\n writer.writeI32LE(value);\n break;\n case \"f32\":\n writer.writeF32LE(value);\n break;\n }\n\n return writer.toBuffer();\n}\n","/**\n * High-level API for reading and modifying FL Studio project files\n * Provides typed access to project metadata, samples, plugins, and time info\n */\n\nimport { EVENT_ID } from \"../generated/events.generated.js\";\nimport { BinaryReader } from \"../io/BinaryReader.js\";\nimport { BinaryWriter } from \"../io/BinaryWriter.js\";\nimport {\n createNumberPayload,\n createTextPayload,\n findFirstEvent,\n type FlpEvent,\n getEventNumber,\n getEventString,\n type ParsedFlp,\n patchEvents,\n} from \"../parser/FlpParser.js\";\n\n// ============================================================================\n// Project Metadata\n// ============================================================================\n\n/**\n * Project metadata (name, description, artist, genre, BPM)\n */\nexport interface ProjectMeta {\n name: string | null;\n description: string | null;\n artist: string | null;\n genre: string | null;\n bpm: number | null;\n}\n\n/**\n * Reads project metadata from a parsed FLP\n */\nexport function readProjectMeta(parsed: ParsedFlp): ProjectMeta {\n const titleEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TITLE);\n const commentsEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_COMMENTS);\n const artistsEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_ARTISTS);\n const genreEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_GENRE);\n const tempoEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TEMPO);\n\n return {\n name: titleEvent ? getEventString(titleEvent, parsed.useUnicode) : null,\n description: commentsEvent ? getEventString(commentsEvent, parsed.useUnicode) : null,\n artist: artistsEvent ? getEventString(artistsEvent, parsed.useUnicode) : null,\n genre: genreEvent ? getEventString(genreEvent, parsed.useUnicode) : null,\n bpm: tempoEvent ? getEventNumber(tempoEvent) / 1000 : null,\n };\n}\n\n/**\n * Writes project metadata to a parsed FLP\n * Only modifies fields that are provided (non-undefined)\n */\nexport function writeProjectMeta(parsed: ParsedFlp, meta: Partial<ProjectMeta>): ParsedFlp {\n return patchEvents(parsed, (event: FlpEvent) => {\n // Patch title\n if (meta.name !== undefined && event.id === EVENT_ID.PROJECT_TITLE) {\n return {\n ...event,\n payload: createTextPayload(meta.name ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch comments/description\n if (meta.description !== undefined && event.id === EVENT_ID.PROJECT_COMMENTS) {\n return {\n ...event,\n payload: createTextPayload(meta.description ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch artists\n if (meta.artist !== undefined && event.id === EVENT_ID.PROJECT_ARTISTS) {\n return {\n ...event,\n payload: createTextPayload(meta.artist ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch genre\n if (meta.genre !== undefined && event.id === EVENT_ID.PROJECT_GENRE) {\n return {\n ...event,\n payload: createTextPayload(meta.genre ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch tempo (stored as BPM * 1000)\n if (meta.bpm && Math.abs(meta.bpm) > 0 && event.id === EVENT_ID.PROJECT_TEMPO) {\n return {\n ...event,\n payload: createNumberPayload(Math.round(Math.abs(meta.bpm) * 1000), \"u32\"),\n };\n }\n\n return event;\n });\n}\n\n// ============================================================================\n// Project Time Info\n// ============================================================================\n\n/**\n * Project creation date and work time\n */\nexport interface ProjectTimeInfo {\n creationDate: Date | null;\n workTimeSeconds: number | null;\n}\n\n// Delphi epoch: December 30, 1899\nconst DELPHI_EPOCH = new Date(1899, 11, 30);\n\n/**\n * Converts Delphi timestamp (days since epoch) to Date\n */\nfunction delphiToDate(days: number): Date {\n const ms = days * 24 * 60 * 60 * 1000;\n return new Date(DELPHI_EPOCH.getTime() + ms);\n}\n\n/**\n * Converts Date to Delphi timestamp (days since epoch)\n */\nfunction dateToDelphiDays(date: Date): number {\n const ms = date.getTime() - DELPHI_EPOCH.getTime();\n return ms / (24 * 60 * 60 * 1000);\n}\n\n/**\n * Reads project time info from a parsed FLP\n * Timestamp event contains: Float64 created_on (days), Float64 time_spent (days)\n */\nexport function readProjectTimeInfo(parsed: ParsedFlp): ProjectTimeInfo {\n const timestampEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TIMESTAMP);\n\n if (!timestampEvent || timestampEvent.payload.length < 16) {\n return {\n creationDate: null,\n workTimeSeconds: null,\n };\n }\n\n const reader = new BinaryReader(timestampEvent.payload);\n const createdOnDays = reader.readF64LE();\n const timeSpentDays = reader.readF64LE();\n\n return {\n creationDate: delphiToDate(createdOnDays),\n workTimeSeconds: timeSpentDays * 24 * 60 * 60, // Convert days to seconds\n };\n}\n\n/**\n * Writes project time info to a parsed FLP\n * Only modifies fields that are provided (non-undefined)\n * \n * If `creationDate` or `workTimeSeconds` is set to `null`, the final value will be set on `0` (Delphi epoch)\n */\nexport function writeProjectTimeInfo(\n parsed: ParsedFlp,\n info: Partial<ProjectTimeInfo>\n): ParsedFlp {\n // Read current values to preserve unmodified fields\n const current = readProjectTimeInfo(parsed);\n\n return patchEvents(parsed, (event: FlpEvent) => {\n if (event.id !== EVENT_ID.PROJECT_TIMESTAMP) {\n return event;\n }\n\n const writer = new BinaryWriter();\n\n // Write creation date\n if (info.creationDate !== undefined && info.creationDate !== null) {\n writer.writeF64LE(dateToDelphiDays(info.creationDate));\n } else if (current.creationDate !== null) {\n writer.writeF64LE(dateToDelphiDays(current.creationDate));\n } else {\n writer.writeF64LE(0);\n }\n\n // Write work time\n if (info.workTimeSeconds !== undefined && info.workTimeSeconds !== null) {\n writer.writeF64LE(info.workTimeSeconds / (24 * 60 * 60)); // Convert seconds to days\n } else if (current.workTimeSeconds !== null) {\n writer.writeF64LE(current.workTimeSeconds / (24 * 60 * 60));\n } else {\n writer.writeF64LE(0);\n }\n\n return {\n ...event,\n payload: writer.toBuffer(),\n };\n });\n}\n\n// ============================================================================\n// Samples\n// ============================================================================\n\n/**\n * Reference to a sample in the project\n */\nexport interface SampleRef {\n /** Index of the event in the events array */\n eventIndex: number;\n /** File path of the sample */\n path: string;\n}\n\n/**\n * Lists all samples in the project\n */\nexport function listSamples(parsed: ParsedFlp): SampleRef[] {\n const samples: SampleRef[] = [];\n\n parsed.events.forEach((event: FlpEvent, index: number) => {\n if (event.id === EVENT_ID.CHANNEL_SAMPLE_PATH) {\n const path = getEventString(event, parsed.useUnicode);\n if (path && path.length > 0) {\n samples.push({\n eventIndex: index,\n path,\n });\n }\n }\n });\n\n return samples;\n}\n\n/**\n * Rewrites sample paths using a mapper function\n */\nexport function rewriteSamplePaths(\n parsed: ParsedFlp,\n mapper: (oldPath: string) => string\n): ParsedFlp {\n return patchEvents(parsed, (event: FlpEvent) => {\n if (event.id !== EVENT_ID.CHANNEL_SAMPLE_PATH) {\n return event;\n }\n\n const oldPath = getEventString(event, parsed.useUnicode);\n if (!oldPath || oldPath.length === 0) {\n return event;\n }\n\n const newPath = mapper(oldPath);\n if (newPath === oldPath) {\n return event;\n }\n\n return {\n ...event,\n payload: createTextPayload(newPath, parsed.useUnicode),\n };\n });\n}\n\n// ============================================================================\n// Plugins (VST)\n// ============================================================================\n\n/**\n * Reference to a plugin in the project\n */\nexport interface PluginRef {\n /** Plugin name (may be null if not available) */\n name: string | null;\n /** Plugin vendor/provider (may be null if not stored in file) */\n vendor: string | null;\n}\n\n/**\n * VST Plugin Event internal IDs (from plugin.py VSTPluginEvent)\n */\nconst VST_EVENT_ID = {\n MIDI: 1,\n FLAGS: 2,\n IO: 30,\n INPUTS: 31,\n OUTPUTS: 32,\n PLUGIN_INFO: 50,\n FOUR_CC: 51,\n GUID: 52,\n STATE: 53,\n NAME: 54,\n PLUGIN_PATH: 55,\n VENDOR: 56,\n} as const;\n\n/**\n * Parses a VST plugin data event to extract name and vendor\n * Based on VSTPluginEvent structure in PyFLP\n */\nfunction parseVstPluginData(payload: Buffer): { name: string | null; vendor: string | null } {\n if (payload.length < 4) {\n return { name: null, vendor: null };\n }\n\n const reader = new BinaryReader(payload);\n\n // First 4 bytes: wrapper marker.\n // Historically seen values include 8/10/11, but newer FL versions can use\n // other markers (e.g. 12). We read and ignore the exact value here because\n // this parser is only called for \"Fruity Wrapper\" plugin data.\n const typeMarker = reader.readU32LE();\n void typeMarker;\n\n let name: string | null = null;\n let vendor: string | null = null;\n\n // Parse sub-events\n while (reader.remaining() >= 12) {\n // At minimum: 4 (id) + 8 (size)\n const subEventId = reader.readU32LE();\n const dataSize = reader.readU32LE(); // Lower 32 bits of size\n const dataSizeHigh = reader.readU32LE(); // Upper 32 bits (usually 0)\n\n // Combine for 64-bit size (though in practice it's always small)\n const actualSize = dataSize + dataSizeHigh * 0x100000000;\n\n if (actualSize > reader.remaining()) {\n break;\n }\n\n const data = reader.readBytes(actualSize);\n\n // Extract name and vendor\n if (subEventId === VST_EVENT_ID.NAME && actualSize > 0) {\n name = data.toString(\"utf8\").replace(/\\0/g, \"\");\n } else if (subEventId === VST_EVENT_ID.VENDOR && actualSize > 0) {\n vendor = data.toString(\"utf8\").replace(/\\0/g, \"\");\n }\n }\n\n return { name, vendor };\n}\n\n/**\n * Lists all plugins (VST and native) in the project\n * Returns plugin name and vendor where available\n */\nexport function listPlugins(parsed: ParsedFlp): PluginRef[] {\n const plugins: PluginRef[] = [];\n const seenPlugins = new Set<string>();\n\n // Track current channel context\n let currentInternalName: string | null = null;\n let currentPluginName: string | null = null;\n\n for (const event of parsed.events) {\n // Track channel new events as boundaries\n if (event.id === EVENT_ID.CHANNEL_NEW) {\n // Reset context for new channel\n if (currentInternalName || currentPluginName) {\n // Save previous channel's plugin if it wasn't a VST (no plugin data)\n const key = `${currentInternalName ?? \"\"}:${currentPluginName ?? \"\"}`;\n if (!seenPlugins.has(key) && currentInternalName && currentInternalName !== \"Fruity Wrapper\") {\n seenPlugins.add(key);\n plugins.push({\n name: currentPluginName || currentInternalName,\n vendor: null, // Native plugins don't store vendor\n });\n }\n }\n currentInternalName = null;\n currentPluginName = null;\n }\n\n // Track internal name (identifies plugin type)\n if (event.id === EVENT_ID.PLUGIN_INTERNAL_NAME) {\n currentInternalName = getEventString(event, parsed.useUnicode);\n }\n\n // Track display name\n if (event.id === EVENT_ID.PLUGIN_NAME) {\n currentPluginName = getEventString(event, parsed.useUnicode);\n }\n\n // Parse VST plugin data\n if (event.id === EVENT_ID.PLUGIN_DATA && currentInternalName === \"Fruity Wrapper\") {\n const vstData = parseVstPluginData(event.payload);\n if (vstData.name) {\n const key = `vst:${vstData.name}:${vstData.vendor ?? \"\"}`;\n if (!seenPlugins.has(key)) {\n seenPlugins.add(key);\n plugins.push({\n name: vstData.name,\n vendor: vstData.vendor,\n });\n }\n }\n // Reset context after processing VST\n currentInternalName = null;\n currentPluginName = null;\n }\n }\n\n // Handle last channel if any\n if (currentInternalName && currentInternalName !== \"Fruity Wrapper\") {\n const key = `${currentInternalName}:${currentPluginName ?? \"\"}`;\n if (!seenPlugins.has(key)) {\n plugins.push({\n name: currentPluginName || currentInternalName,\n vendor: null,\n });\n }\n }\n\n return plugins;\n}\n\n// ============================================================================\n// FL Version\n// ============================================================================\n\n/**\n * Gets the FL Studio version string from the project\n */\nexport function getFlVersion(parsed: ParsedFlp): string {\n return parsed.flVersion;\n}\n\n/**\n * Gets the PPQ (Pulses Per Quarter note) from the header\n */\nexport function getPPQ(parsed: ParsedFlp): number {\n // PPQ is at offset 12-13 in the header (bytes 12-13 of the 14-byte FLhd chunk)\n const reader = new BinaryReader(parsed.headerChunkBytes);\n reader.seek(12);\n return reader.readU16LE();\n}\n"],"mappings":"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","names":["protobuf","import_smart_buffer","import_minimal","protobuf","headerSize"]}
package/dist/index.js CHANGED
@@ -268,11 +268,13 @@ function getEventFixedSize(eventId) {
268
268
  }
269
269
 
270
270
  // src/io/BinaryReader.ts
271
- import * as protobuf from "protobufjs/minimal.js";
271
+ import protobuf from "protobufjs/minimal.js";
272
272
  import { SmartBuffer } from "smart-buffer";
273
273
  var BinaryReader = class {
274
274
  sb;
275
+ source;
275
276
  constructor(buffer) {
277
+ this.source = buffer;
276
278
  this.sb = SmartBuffer.fromBuffer(buffer);
277
279
  }
278
280
  /**
@@ -370,7 +372,7 @@ var BinaryReader = class {
370
372
  * FL Studio uses this encoding for TEXT and DATA event sizes
371
373
  */
372
374
  readVarInt() {
373
- const remainingBuffer = this.sb.toBuffer().subarray(this.sb.readOffset);
375
+ const remainingBuffer = this.source.subarray(this.sb.readOffset);
374
376
  const reader = protobuf.Reader.create(remainingBuffer);
375
377
  const value = reader.uint32();
376
378
  this.sb.readOffset += reader.pos;
@@ -415,13 +417,13 @@ var BinaryReader = class {
415
417
  * Returns the internal buffer
416
418
  */
417
419
  toBuffer() {
418
- return this.sb.toBuffer();
420
+ return this.source;
419
421
  }
420
422
  };
421
423
 
422
424
  // src/io/BinaryWriter.ts
423
425
  import { SmartBuffer as SmartBuffer2 } from "smart-buffer";
424
- import * as protobuf2 from "protobufjs/minimal.js";
426
+ import protobuf2 from "protobufjs/minimal.js";
425
427
  var BinaryWriter = class {
426
428
  sb;
427
429
  constructor() {
@@ -566,6 +568,7 @@ function parseFlp(buffer) {
566
568
  const events = [];
567
569
  let flVersion = "0.0.0";
568
570
  let useUnicode = false;
571
+ let detectedVersion = false;
569
572
  const eventsStart = reader.tell();
570
573
  const eventsEnd = eventsStart + eventsSize;
571
574
  while (reader.tell() < eventsEnd) {
@@ -597,12 +600,17 @@ function parseFlp(buffer) {
597
600
  payload: Buffer.from(payload)
598
601
  };
599
602
  events.push(event);
600
- if (eventId === EVENT_ID.PROJECT_FL_VERSION) {
601
- flVersion = payload.toString("ascii").replace(/\0/g, "");
602
- const parts = flVersion.split(".").map((p) => parseInt(p, 10));
603
- const major = parts[0] ?? 0;
604
- const minor = parts[1] ?? 0;
605
- useUnicode = parts.length >= 2 && (major > 11 || major === 11 && minor >= 5);
603
+ if (eventId === EVENT_ID.PROJECT_FL_VERSION && !detectedVersion) {
604
+ const candidate = payload.toString("ascii").replace(/\0/g, "").trim();
605
+ const isSemverLike = /^\d+(\.\d+)+$/.test(candidate);
606
+ if (isSemverLike) {
607
+ flVersion = candidate;
608
+ const parts = flVersion.split(".").map((p) => parseInt(p, 10));
609
+ const major = parts[0] ?? 0;
610
+ const minor = parts[1] ?? 0;
611
+ useUnicode = parts.length >= 2 && (major > 11 || major === 11 && minor >= 5);
612
+ detectedVersion = true;
613
+ }
606
614
  }
607
615
  }
608
616
  let trailingBytes;
@@ -640,9 +648,13 @@ function serializeFlp(parsed) {
640
648
  }
641
649
  function serializeEvent(event) {
642
650
  const writer = new BinaryWriter();
643
- writer.writeU8(event.id);
644
- if (event.id >= TEXT) {
645
- writer.writeVarInt(event.payload.length);
651
+ if (event.header.length > 0) {
652
+ writer.writeBytes(event.header);
653
+ } else {
654
+ writer.writeU8(event.id);
655
+ if (event.id >= TEXT) {
656
+ writer.writeVarInt(event.payload.length);
657
+ }
646
658
  }
647
659
  writer.writeBytes(event.payload);
648
660
  return writer.toBuffer();
@@ -913,9 +925,7 @@ function parseVstPluginData(payload) {
913
925
  }
914
926
  const reader = new BinaryReader(payload);
915
927
  const typeMarker = reader.readU32LE();
916
- if (typeMarker !== 8 && typeMarker !== 10 && typeMarker !== 11) {
917
- return { name: null, vendor: null };
918
- }
928
+ void typeMarker;
919
929
  let name = null;
920
930
  let vendor = null;
921
931
  while (reader.remaining() >= 12) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generated/events.generated.ts","../src/io/BinaryReader.ts","../src/io/BinaryWriter.ts","../src/parser/FlpParser.ts","../src/api/ProjectApi.ts"],"sourcesContent":["/**\r\n * Auto-generated event constants from PyFLP\r\n * FL Studio .flp file event IDs and types\r\n *\r\n * Event ID ranges:\r\n * - BYTE (0-63): 1 byte data\r\n * - WORD (64-127): 2 bytes data\r\n * - DWORD (128-191): 4 bytes data\r\n * - TEXT (192-207): Variable length strings (+ some IDs in DATA range)\r\n * - DATA (208+): Variable length binary data\r\n */\r\n\r\n// Base offsets for event ID ranges\r\nexport const BYTE = 0;\r\nexport const WORD = 64;\r\nexport const DWORD = 128;\r\nexport const TEXT = 192;\r\nexport const DATA = 208;\r\n\r\n// IDs for TEXT events that use UTF-16LE in newer FL versions\r\nexport const NEW_TEXT_IDS = [\r\n TEXT + 49, // ArrangementID.Name\r\n TEXT + 39, // DisplayGroupID.Name\r\n TEXT + 47, // TrackID.Name\r\n] as const;\r\n\r\n/**\r\n * Event IDs used by FL Studio project files\r\n * Based on PyFLP's event definitions\r\n */\r\nexport const EVENT_ID = {\r\n // === Project IDs (from project.py) ===\r\n PROJECT_LOOP_ACTIVE: 9,\r\n PROJECT_SHOW_INFO: 10,\r\n PROJECT__VOLUME: 12,\r\n PROJECT_PAN_LAW: 23,\r\n PROJECT_LICENSED: 28,\r\n PROJECT__TEMPO_COARSE: WORD + 2, // 66\r\n PROJECT_PITCH: WORD + 16, // 80\r\n PROJECT__TEMPO_FINE: WORD + 29, // 93\r\n PROJECT_CUR_GROUP_ID: DWORD + 18,\r\n PROJECT_TEMPO: DWORD + 28,\r\n PROJECT_FL_BUILD: DWORD + 31,\r\n PROJECT_TITLE: TEXT + 2, // 194\r\n PROJECT_COMMENTS: TEXT + 3, // 195\r\n PROJECT_URL: TEXT + 5, // 197\r\n PROJECT__RTF_COMMENTS: TEXT + 6, // 198\r\n PROJECT_FL_VERSION: TEXT + 7, // 199\r\n PROJECT_LICENSEE: TEXT + 8, // 200\r\n PROJECT_DATA_PATH: TEXT + 10, // 202\r\n PROJECT_GENRE: TEXT + 14, // 206\r\n PROJECT_ARTISTS: TEXT + 15, // 207\r\n PROJECT_TIMESTAMP: DATA + 29, // 237\r\n\r\n // === Channel IDs (from channel.py) ===\r\n CHANNEL_IS_ENABLED: 0,\r\n CHANNEL__VOL_BYTE: 2,\r\n CHANNEL__PAN_BYTE: 3,\r\n CHANNEL_ZIPPED: 15,\r\n CHANNEL_PING_PONG_LOOP: 20,\r\n CHANNEL_TYPE: 21,\r\n CHANNEL_ROUTED_TO: 22,\r\n CHANNEL_IS_LOCKED: 32,\r\n CHANNEL_NEW: WORD,\r\n CHANNEL_FREQ_TILT: WORD + 5, // 69\r\n CHANNEL_FX_FLAGS: WORD + 6, // 70\r\n CHANNEL_CUTOFF: WORD + 7, // 71\r\n CHANNEL__VOL_WORD: WORD + 8, // 72\r\n CHANNEL__PAN_WORD: WORD + 9, // 73\r\n CHANNEL_PREAMP: WORD + 10, // 74\r\n CHANNEL_FADE_OUT: WORD + 11, // 75\r\n CHANNEL_FADE_IN: WORD + 12, // 76\r\n CHANNEL_RESONANCE: WORD + 19, // 83\r\n CHANNEL_STEREO_DELAY: WORD + 21, // 85\r\n CHANNEL_POGO: WORD + 22, // 86\r\n CHANNEL_TIME_SHIFT: WORD + 25, // 89\r\n CHANNEL_CHILDREN: WORD + 30, // 94\r\n CHANNEL_SWING: WORD + 33, // 97\r\n CHANNEL_RING_MOD: DWORD + 3,\r\n CHANNEL_CUT_GROUP: DWORD + 4,\r\n CHANNEL_ROOT_NOTE: DWORD + 7,\r\n CHANNEL_DELAY_MOD_XY: DWORD + 10,\r\n CHANNEL_REVERB: DWORD + 11,\r\n CHANNEL__STRETCH_TIME: DWORD + 12,\r\n CHANNEL_FINE_TUNE: DWORD + 14,\r\n CHANNEL_SAMPLER_FLAGS: DWORD + 15,\r\n CHANNEL_LAYER_FLAGS: DWORD + 16,\r\n CHANNEL_GROUP_NUM: DWORD + 17,\r\n CHANNEL_AU_SAMPLE_RATE: DWORD + 25,\r\n CHANNEL__NAME: TEXT,\r\n CHANNEL_SAMPLE_PATH: TEXT + 4, // 196\r\n CHANNEL_DELAY: DATA + 1, // 209\r\n CHANNEL_PARAMETERS: DATA + 7, // 215\r\n CHANNEL_ENVELOPE_LFO: DATA + 10, // 218\r\n CHANNEL_LEVELS: DATA + 11, // 219\r\n CHANNEL_POLYPHONY: DATA + 13, // 221\r\n CHANNEL_TRACKING: DATA + 20, // 228\r\n CHANNEL_LEVEL_ADJUSTS: DATA + 21, // 229\r\n CHANNEL_AUTOMATION: DATA + 26, // 234\r\n\r\n // === Plugin IDs (from plugin.py) ===\r\n PLUGIN_COLOR: DWORD,\r\n PLUGIN_ICON: DWORD + 27,\r\n PLUGIN_INTERNAL_NAME: TEXT + 9, // 201\r\n PLUGIN_NAME: TEXT + 11, // 203\r\n PLUGIN_WRAPPER: DATA + 4, // 212\r\n PLUGIN_DATA: DATA + 5, // 213\r\n\r\n // === Display Group IDs ===\r\n DISPLAY_GROUP_NAME: TEXT + 39, // 231\r\n\r\n // === Rack IDs ===\r\n RACK_SWING: 11,\r\n RACK__FIT_TO_STEPS: 13,\r\n RACK_WINDOW_HEIGHT: DWORD + 5,\r\n\r\n // === Mixer IDs (from mixer.py) ===\r\n MIXER_SLOT_INDEX: WORD + 34, // 98\r\n MIXER_INSERT_ICON: WORD + 31, // 95\r\n MIXER_INSERT_OUTPUT: DWORD + 19,\r\n MIXER_INSERT_COLOR: DWORD + 21,\r\n MIXER_INSERT_INPUT: DWORD + 26,\r\n MIXER_INSERT_NAME: TEXT + 12, // 204\r\n MIXER_INSERT_ROUTING: DATA + 27, // 235\r\n MIXER_INSERT_FLAGS: DATA + 28, // 236\r\n} as const;\r\n\r\n/**\r\n * Event data type classification\r\n */\r\nexport type EventKind =\r\n | \"u8\"\r\n | \"i8\"\r\n | \"u16\"\r\n | \"i16\"\r\n | \"u32\"\r\n | \"i32\"\r\n | \"f32\"\r\n | \"text\"\r\n | \"data\"\r\n | \"unknown\";\r\n\r\n/**\r\n * Maps event IDs to their data type\r\n * Used for proper serialization/deserialization\r\n */\r\nexport const EVENT_KIND: Record<number, EventKind> = {\r\n // Project events\r\n [EVENT_ID.PROJECT_LOOP_ACTIVE]: \"u8\",\r\n [EVENT_ID.PROJECT_SHOW_INFO]: \"u8\",\r\n [EVENT_ID.PROJECT__VOLUME]: \"u8\",\r\n [EVENT_ID.PROJECT_PAN_LAW]: \"u8\",\r\n [EVENT_ID.PROJECT_LICENSED]: \"u8\",\r\n [EVENT_ID.PROJECT__TEMPO_COARSE]: \"u16\",\r\n [EVENT_ID.PROJECT_PITCH]: \"i16\",\r\n [EVENT_ID.PROJECT__TEMPO_FINE]: \"u16\",\r\n [EVENT_ID.PROJECT_CUR_GROUP_ID]: \"i32\",\r\n [EVENT_ID.PROJECT_TEMPO]: \"u32\",\r\n [EVENT_ID.PROJECT_FL_BUILD]: \"u32\",\r\n [EVENT_ID.PROJECT_TITLE]: \"text\",\r\n [EVENT_ID.PROJECT_COMMENTS]: \"text\",\r\n [EVENT_ID.PROJECT_URL]: \"text\",\r\n [EVENT_ID.PROJECT__RTF_COMMENTS]: \"text\",\r\n [EVENT_ID.PROJECT_FL_VERSION]: \"text\",\r\n [EVENT_ID.PROJECT_LICENSEE]: \"text\",\r\n [EVENT_ID.PROJECT_DATA_PATH]: \"text\",\r\n [EVENT_ID.PROJECT_GENRE]: \"text\",\r\n [EVENT_ID.PROJECT_ARTISTS]: \"text\",\r\n [EVENT_ID.PROJECT_TIMESTAMP]: \"data\",\r\n\r\n // Channel events\r\n [EVENT_ID.CHANNEL_IS_ENABLED]: \"u8\",\r\n [EVENT_ID.CHANNEL__VOL_BYTE]: \"u8\",\r\n [EVENT_ID.CHANNEL__PAN_BYTE]: \"u8\",\r\n [EVENT_ID.CHANNEL_ZIPPED]: \"u8\",\r\n [EVENT_ID.CHANNEL_PING_PONG_LOOP]: \"u8\",\r\n [EVENT_ID.CHANNEL_TYPE]: \"u8\",\r\n [EVENT_ID.CHANNEL_ROUTED_TO]: \"i8\",\r\n [EVENT_ID.CHANNEL_IS_LOCKED]: \"u8\",\r\n [EVENT_ID.CHANNEL_NEW]: \"u16\",\r\n [EVENT_ID.CHANNEL_FREQ_TILT]: \"u16\",\r\n [EVENT_ID.CHANNEL_FX_FLAGS]: \"u16\",\r\n [EVENT_ID.CHANNEL_CUTOFF]: \"u16\",\r\n [EVENT_ID.CHANNEL__VOL_WORD]: \"u16\",\r\n [EVENT_ID.CHANNEL__PAN_WORD]: \"u16\",\r\n [EVENT_ID.CHANNEL_PREAMP]: \"u16\",\r\n [EVENT_ID.CHANNEL_FADE_OUT]: \"u16\",\r\n [EVENT_ID.CHANNEL_FADE_IN]: \"u16\",\r\n [EVENT_ID.CHANNEL_RESONANCE]: \"u16\",\r\n [EVENT_ID.CHANNEL_STEREO_DELAY]: \"u16\",\r\n [EVENT_ID.CHANNEL_POGO]: \"u16\",\r\n [EVENT_ID.CHANNEL_TIME_SHIFT]: \"u16\",\r\n [EVENT_ID.CHANNEL_CHILDREN]: \"u16\",\r\n [EVENT_ID.CHANNEL_SWING]: \"u16\",\r\n [EVENT_ID.CHANNEL_RING_MOD]: \"u32\",\r\n [EVENT_ID.CHANNEL_CUT_GROUP]: \"u32\",\r\n [EVENT_ID.CHANNEL_ROOT_NOTE]: \"u32\",\r\n [EVENT_ID.CHANNEL_DELAY_MOD_XY]: \"u32\",\r\n [EVENT_ID.CHANNEL_REVERB]: \"u32\",\r\n [EVENT_ID.CHANNEL__STRETCH_TIME]: \"f32\",\r\n [EVENT_ID.CHANNEL_FINE_TUNE]: \"i32\",\r\n [EVENT_ID.CHANNEL_SAMPLER_FLAGS]: \"u32\",\r\n [EVENT_ID.CHANNEL_LAYER_FLAGS]: \"u32\",\r\n [EVENT_ID.CHANNEL_GROUP_NUM]: \"i32\",\r\n [EVENT_ID.CHANNEL_AU_SAMPLE_RATE]: \"u32\",\r\n [EVENT_ID.CHANNEL__NAME]: \"text\",\r\n [EVENT_ID.CHANNEL_SAMPLE_PATH]: \"text\",\r\n [EVENT_ID.CHANNEL_DELAY]: \"data\",\r\n [EVENT_ID.CHANNEL_PARAMETERS]: \"data\",\r\n [EVENT_ID.CHANNEL_ENVELOPE_LFO]: \"data\",\r\n [EVENT_ID.CHANNEL_LEVELS]: \"data\",\r\n [EVENT_ID.CHANNEL_POLYPHONY]: \"data\",\r\n [EVENT_ID.CHANNEL_TRACKING]: \"data\",\r\n [EVENT_ID.CHANNEL_LEVEL_ADJUSTS]: \"data\",\r\n [EVENT_ID.CHANNEL_AUTOMATION]: \"data\",\r\n\r\n // Plugin events\r\n [EVENT_ID.PLUGIN_COLOR]: \"u32\",\r\n [EVENT_ID.PLUGIN_ICON]: \"u32\",\r\n [EVENT_ID.PLUGIN_INTERNAL_NAME]: \"text\",\r\n [EVENT_ID.PLUGIN_NAME]: \"text\",\r\n [EVENT_ID.PLUGIN_WRAPPER]: \"data\",\r\n [EVENT_ID.PLUGIN_DATA]: \"data\",\r\n\r\n // Display Group events\r\n [EVENT_ID.DISPLAY_GROUP_NAME]: \"data\",\r\n\r\n // Rack events\r\n [EVENT_ID.RACK_SWING]: \"u8\",\r\n [EVENT_ID.RACK__FIT_TO_STEPS]: \"u8\",\r\n [EVENT_ID.RACK_WINDOW_HEIGHT]: \"u32\",\r\n\r\n // Mixer events\r\n [EVENT_ID.MIXER_SLOT_INDEX]: \"u16\",\r\n [EVENT_ID.MIXER_INSERT_ICON]: \"i16\",\r\n [EVENT_ID.MIXER_INSERT_OUTPUT]: \"i32\",\r\n [EVENT_ID.MIXER_INSERT_COLOR]: \"u32\",\r\n [EVENT_ID.MIXER_INSERT_INPUT]: \"i32\",\r\n [EVENT_ID.MIXER_INSERT_NAME]: \"text\",\r\n [EVENT_ID.MIXER_INSERT_ROUTING]: \"data\",\r\n [EVENT_ID.MIXER_INSERT_FLAGS]: \"data\",\r\n\r\n};\r\n\r\n/**\r\n * Determines the event kind based on ID\r\n * Falls back to range-based detection if not explicitly mapped\r\n */\r\nexport function getEventKind(eventId: number): EventKind {\r\n // Check explicit mapping first\r\n const mapped = EVENT_KIND[eventId];\r\n if (mapped !== undefined) {\r\n return mapped;\r\n }\r\n\r\n // Fall back to range-based detection\r\n if (eventId < WORD) {\r\n return \"u8\";\r\n } else if (eventId < DWORD) {\r\n return \"u16\";\r\n } else if (eventId < TEXT) {\r\n return \"u32\";\r\n } else if (eventId < DATA || NEW_TEXT_IDS.includes(eventId as 241 | 231 | 239)) {\r\n return \"text\";\r\n } else {\r\n return \"data\";\r\n }\r\n}\r\n\r\n/**\r\n * Returns the fixed size for BYTE/WORD/DWORD events\r\n * Returns -1 for variable-length events (TEXT/DATA)\r\n */\r\nexport function getEventFixedSize(eventId: number): number {\r\n if (eventId < WORD) {\r\n return 1;\r\n } else if (eventId < DWORD) {\r\n return 2;\r\n } else if (eventId < TEXT) {\r\n return 4;\r\n }\r\n return -1; // Variable length\r\n}\r\n","/**\n * Binary reader for FL Studio .flp files\n * Uses smart-buffer for binary reading and protobufjs for VarInt decoding\n */\n\nimport * as protobuf from \"protobufjs/minimal.js\";\nimport { SmartBuffer } from \"smart-buffer\";\n\nexport class BinaryReader {\n private sb: SmartBuffer;\n\n constructor(buffer: Buffer) {\n this.sb = SmartBuffer.fromBuffer(buffer);\n }\n\n /**\n * Returns current read position\n */\n tell(): number {\n return this.sb.readOffset;\n }\n\n /**\n * Seeks to an absolute position\n */\n seek(pos: number): void {\n this.sb.readOffset = pos;\n }\n\n /**\n * Seeks relative to current position\n */\n skip(offset: number): void {\n this.sb.readOffset += offset;\n }\n\n /**\n * Returns total buffer length\n */\n length(): number {\n return this.sb.length;\n }\n\n /**\n * Returns remaining bytes to read\n */\n remaining(): number {\n return this.sb.remaining();\n }\n\n /**\n * Returns true if there are more bytes to read\n */\n hasMore(): boolean {\n return this.sb.remaining() > 0;\n }\n\n /**\n * Reads an unsigned 8-bit integer\n */\n readU8(): number {\n return this.sb.readUInt8();\n }\n\n /**\n * Reads a signed 8-bit integer\n */\n readI8(): number {\n return this.sb.readInt8();\n }\n\n /**\n * Reads an unsigned 16-bit integer (little-endian)\n */\n readU16LE(): number {\n return this.sb.readUInt16LE();\n }\n\n /**\n * Reads a signed 16-bit integer (little-endian)\n */\n readI16LE(): number {\n return this.sb.readInt16LE();\n }\n\n /**\n * Reads an unsigned 32-bit integer (little-endian)\n */\n readU32LE(): number {\n return this.sb.readUInt32LE();\n }\n\n /**\n * Reads a signed 32-bit integer (little-endian)\n */\n readI32LE(): number {\n return this.sb.readInt32LE();\n }\n\n /**\n * Reads a 32-bit float (little-endian)\n */\n readF32LE(): number {\n return this.sb.readFloatLE();\n }\n\n /**\n * Reads a 64-bit float (little-endian)\n */\n readF64LE(): number {\n return this.sb.readDoubleLE();\n }\n\n /**\n * Reads raw bytes\n */\n readBytes(length: number): Buffer {\n return this.sb.readBuffer(length);\n }\n\n /**\n * Reads a VarInt (variable-length integer) using protobufjs\n * FL Studio uses this encoding for TEXT and DATA event sizes\n */\n readVarInt(): number {\n // Get remaining buffer from current position\n const remainingBuffer = this.sb.toBuffer().subarray(this.sb.readOffset);\n const reader = protobuf.Reader.create(remainingBuffer);\n const value = reader.uint32();\n // Advance our position by how many bytes protobufjs consumed\n this.sb.readOffset += reader.pos;\n return value;\n }\n\n /**\n * Reads an ASCII string (null-terminated)\n */\n readAsciiString(length: number): string {\n const str = this.sb.readString(length, \"ascii\");\n // Remove null terminator if present\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n\n /**\n * Reads a UTF-16LE string (null-terminated)\n */\n readUtf16LEString(length: number): string {\n const bytes = this.sb.readBuffer(length);\n const str = bytes.toString(\"utf16le\");\n // Remove null terminator if present\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n\n /**\n * Peeks at bytes without advancing position\n */\n peek(length: number): Buffer {\n const pos = this.sb.readOffset;\n const data = this.sb.readBuffer(length);\n this.sb.readOffset = pos;\n return data;\n }\n\n /**\n * Peeks at a single byte without advancing position\n */\n peekU8(): number {\n const pos = this.sb.readOffset;\n const value = this.sb.readUInt8();\n this.sb.readOffset = pos;\n return value;\n }\n\n /**\n * Returns the internal buffer\n */\n toBuffer(): Buffer {\n return this.sb.toBuffer();\n }\n}\n","/**\n * Binary writer for FL Studio .flp files\n * Uses smart-buffer for binary writing and protobufjs for VarInt encoding\n */\n\nimport { SmartBuffer } from \"smart-buffer\";\nimport * as protobuf from \"protobufjs/minimal.js\";\n\nexport class BinaryWriter {\n private sb: SmartBuffer;\n\n constructor() {\n this.sb = new SmartBuffer();\n }\n\n /**\n * Returns current write position (total bytes written)\n */\n tell(): number {\n return this.sb.writeOffset;\n }\n\n /**\n * Writes an unsigned 8-bit integer\n */\n writeU8(value: number): void {\n this.sb.writeUInt8(value & 0xff);\n }\n\n /**\n * Writes a signed 8-bit integer\n */\n writeI8(value: number): void {\n this.sb.writeInt8(value);\n }\n\n /**\n * Writes an unsigned 16-bit integer (little-endian)\n */\n writeU16LE(value: number): void {\n this.sb.writeUInt16LE(value & 0xffff);\n }\n\n /**\n * Writes a signed 16-bit integer (little-endian)\n */\n writeI16LE(value: number): void {\n this.sb.writeInt16LE(value);\n }\n\n /**\n * Writes an unsigned 32-bit integer (little-endian)\n */\n writeU32LE(value: number): void {\n this.sb.writeUInt32LE(value >>> 0);\n }\n\n /**\n * Writes a signed 32-bit integer (little-endian)\n */\n writeI32LE(value: number): void {\n this.sb.writeInt32LE(value);\n }\n\n /**\n * Writes a 32-bit float (little-endian)\n */\n writeF32LE(value: number): void {\n this.sb.writeFloatLE(value);\n }\n\n /**\n * Writes a 64-bit float (little-endian)\n */\n writeF64LE(value: number): void {\n this.sb.writeDoubleLE(value);\n }\n\n /**\n * Writes raw bytes\n */\n writeBytes(data: Buffer): void {\n this.sb.writeBuffer(data);\n }\n\n /**\n * Writes a VarInt (variable-length integer) using protobufjs\n * FL Studio uses this encoding for TEXT and DATA event sizes\n */\n writeVarInt(value: number): void {\n const writer = protobuf.Writer.create();\n writer.uint32(value);\n const encoded = writer.finish();\n this.sb.writeBuffer(Buffer.from(encoded));\n }\n\n /**\n * Writes an ASCII string (null-terminated)\n */\n writeAsciiString(str: string): void {\n this.sb.writeString(str + \"\\0\", \"ascii\");\n }\n\n /**\n * Writes a UTF-16LE string (null-terminated)\n */\n writeUtf16LEString(str: string): void {\n const buf = Buffer.from(str + \"\\0\", \"utf16le\");\n this.sb.writeBuffer(buf);\n }\n\n /**\n * Gets the final buffer\n */\n toBuffer(): Buffer {\n return this.sb.toBuffer();\n }\n}\n\n/**\n * Encodes a VarInt to a Buffer using protobufjs (standalone utility)\n */\nexport function encodeVarInt(value: number): Buffer {\n const writer = protobuf.Writer.create();\n writer.uint32(value);\n return Buffer.from(writer.finish());\n}\n\n/**\n * Calculates the byte size needed for a VarInt\n */\nexport function varIntSize(value: number): number {\n if (value < 0x80) return 1;\n if (value < 0x4000) return 2;\n if (value < 0x200000) return 3;\n if (value < 0x10000000) return 4;\n return 5;\n}\n","/**\n * FL Studio .flp file parser and serializer\n * Conservative patch approach: preserves all unknown data byte-for-byte\n */\n\nimport { DWORD, EVENT_ID, TEXT, WORD, getEventKind } from \"../generated/events.generated.js\";\nimport { BinaryReader } from \"../io/BinaryReader.js\";\nimport { BinaryWriter } from \"../io/BinaryWriter.js\";\n\n/**\n * Represents a single event in an FLP file\n * Stores raw bytes to enable conservative patching\n */\nexport interface FlpEvent {\n /** Event ID (0-255) */\n id: number;\n /** Data type classification */\n kind: \"u8\" | \"i8\" | \"u16\" | \"i16\" | \"u32\" | \"i32\" | \"f32\" | \"text\" | \"data\" | \"unknown\";\n /** Original header bytes (ID + size for variable-length events) */\n header: Buffer;\n /** Event payload data */\n payload: Buffer;\n}\n\n/**\n * Represents a parsed FLP file\n * Contains all data needed for conservative round-trip\n */\nexport interface ParsedFlp {\n /** FLhd chunk (header) - 14 bytes total */\n headerChunkBytes: Buffer;\n /** FLdt chunk header (magic + size placeholder) - 8 bytes */\n fldtHeaderBytes: Buffer;\n /** All events from the FLdt chunk */\n events: FlpEvent[];\n /** Any trailing bytes after events (should be empty) */\n trailingBytes: Buffer | undefined;\n /** Detected FL version for string encoding */\n flVersion: string;\n /** Whether to use UTF-16LE for strings (FL >= 11.5) */\n useUnicode: boolean;\n}\n\n/**\n * Header structure constants\n */\nconst FLP_HEADER_MAGIC = \"FLhd\";\nconst FLP_DATA_MAGIC = \"FLdt\";\nconst FLP_HEADER_SIZE = 6; // Expected header chunk size\n\n/**\n * Parses an FL Studio project file\n *\n * @param buffer - Raw file data\n * @returns Parsed FLP structure\n * @throws Error if file is corrupted or invalid\n */\nexport function parseFlp(buffer: Buffer): ParsedFlp {\n const reader = new BinaryReader(buffer);\n\n // Read FLhd header chunk\n const headerMagic = reader.readBytes(4).toString(\"ascii\");\n if (headerMagic !== FLP_HEADER_MAGIC) {\n throw new Error(`Invalid header magic: expected 'FLhd', got '${headerMagic}'`);\n }\n\n const headerSize = reader.readU32LE();\n if (headerSize !== FLP_HEADER_SIZE) {\n throw new Error(`Invalid header size: expected ${FLP_HEADER_SIZE}, got ${headerSize}`);\n }\n\n const format = reader.readI16LE();\n const channelCount = reader.readU16LE();\n const ppq = reader.readU16LE();\n\n // Validate format (0 = normal project)\n if (format < -1 || format > 0x50) {\n throw new Error(`Invalid file format: ${format}`);\n }\n\n // Store complete header chunk (14 bytes)\n const headerChunkBytes = buffer.subarray(0, 14);\n\n // Read FLdt data chunk header\n const dataMagic = reader.readBytes(4).toString(\"ascii\");\n if (dataMagic !== FLP_DATA_MAGIC) {\n throw new Error(`Invalid data magic: expected 'FLdt', got '${dataMagic}'`);\n }\n\n const eventsSize = reader.readU32LE();\n\n // Validate events size\n const expectedFileSize = 22 + eventsSize; // 14 (FLhd) + 8 (FLdt header) + events\n if (buffer.length !== expectedFileSize) {\n throw new Error(\n `Data chunk size mismatch: expected file size ${expectedFileSize}, got ${buffer.length}`\n );\n }\n\n // Store FLdt header (8 bytes)\n const fldtHeaderBytes = buffer.subarray(14, 22);\n\n // Parse events\n const events: FlpEvent[] = [];\n let flVersion = \"0.0.0\";\n let useUnicode = false;\n\n const eventsStart = reader.tell();\n const eventsEnd = eventsStart + eventsSize;\n\n while (reader.tell() < eventsEnd) {\n const eventStart = reader.tell();\n const eventId = reader.readU8();\n\n let payloadSize: number;\n let headerSize: number;\n\n // Determine payload size based on event ID range\n if (eventId < WORD) {\n // BYTE range: 1 byte payload\n payloadSize = 1;\n headerSize = 1; // Just the ID\n } else if (eventId < DWORD) {\n // WORD range: 2 bytes payload\n payloadSize = 2;\n headerSize = 1;\n } else if (eventId < TEXT) {\n // DWORD range: 4 bytes payload\n payloadSize = 4;\n headerSize = 1;\n } else {\n // TEXT/DATA range: VarInt-encoded size\n const sizeStart = reader.tell();\n payloadSize = reader.readVarInt();\n headerSize = 1 + (reader.tell() - sizeStart); // ID + VarInt bytes\n }\n\n // Read payload\n const payload = reader.readBytes(payloadSize);\n\n // Extract header from original buffer for byte-perfect round-trip\n const header = buffer.subarray(eventStart, eventStart + headerSize);\n\n // Determine event kind\n const kind = getEventKind(eventId);\n\n const event: FlpEvent = {\n id: eventId,\n kind,\n header: Buffer.from(header),\n payload: Buffer.from(payload),\n };\n\n events.push(event);\n\n // Detect FL version from FLVersion event\n if (eventId === EVENT_ID.PROJECT_FL_VERSION) {\n flVersion = payload.toString(\"ascii\").replace(/\\0/g, \"\");\n const parts = flVersion.split(\".\").map((p) => parseInt(p, 10));\n const major = parts[0] ?? 0;\n const minor = parts[1] ?? 0;\n useUnicode = parts.length >= 2 && (major > 11 || (major === 11 && minor >= 5));\n }\n }\n\n // Check for trailing bytes\n let trailingBytes: Buffer | undefined;\n if (reader.remaining() > 0) {\n trailingBytes = reader.readBytes(reader.remaining());\n }\n\n return {\n headerChunkBytes: Buffer.from(headerChunkBytes),\n fldtHeaderBytes: Buffer.from(fldtHeaderBytes),\n events,\n trailingBytes,\n flVersion,\n useUnicode,\n };\n}\n\n/**\n * Serializes a parsed FLP back to a buffer\n * Recalculates FLdt chunk size if events have changed\n *\n * @param parsed - Parsed FLP structure\n * @returns Serialized buffer\n */\nexport function serializeFlp(parsed: ParsedFlp): Buffer {\n const writer = new BinaryWriter();\n\n // Write FLhd header chunk (preserved byte-for-byte)\n writer.writeBytes(parsed.headerChunkBytes);\n\n // Calculate total events size\n let eventsSize = 0;\n for (const event of parsed.events) {\n eventsSize += serializeEvent(event).length;\n }\n\n // Add trailing bytes if present\n if (parsed.trailingBytes) {\n eventsSize += parsed.trailingBytes.length;\n }\n\n // Write FLdt header with updated size\n writer.writeBytes(Buffer.from(FLP_DATA_MAGIC, \"ascii\"));\n writer.writeU32LE(eventsSize);\n\n // Write all events\n for (const event of parsed.events) {\n writer.writeBytes(serializeEvent(event));\n }\n\n // Write trailing bytes if present\n if (parsed.trailingBytes) {\n writer.writeBytes(parsed.trailingBytes);\n }\n\n return writer.toBuffer();\n}\n\n/**\n * Serializes a single event to bytes\n * Uses original header for unchanged events, recalculates for modified ones\n */\nfunction serializeEvent(event: FlpEvent): Buffer {\n const writer = new BinaryWriter();\n\n // Write event ID\n writer.writeU8(event.id);\n\n // For TEXT/DATA events, we need to write the VarInt size\n if (event.id >= TEXT) {\n writer.writeVarInt(event.payload.length);\n }\n\n // Write payload\n writer.writeBytes(event.payload);\n\n return writer.toBuffer();\n}\n\n/**\n * Creates a new event with the given ID and payload\n */\nexport function createEvent(id: number, payload: Buffer): FlpEvent {\n const kind = getEventKind(id);\n\n // Build header\n const headerWriter = new BinaryWriter();\n headerWriter.writeU8(id);\n\n if (id >= TEXT) {\n headerWriter.writeVarInt(payload.length);\n }\n\n return {\n id,\n kind,\n header: headerWriter.toBuffer(),\n payload: Buffer.from(payload),\n };\n}\n\n/**\n * Applies a patcher function to all events\n * Returns a new ParsedFlp with patched events\n *\n * @param parsed - Original parsed FLP\n * @param patcher - Function that transforms each event\n * @returns New ParsedFlp with patched events\n */\nexport function patchEvents(\n parsed: ParsedFlp,\n patcher: (event: FlpEvent, index: number) => FlpEvent\n): ParsedFlp {\n const patchedEvents = parsed.events.map((event, index) => {\n const patched = patcher(event, index);\n\n // If payload changed, recalculate header for TEXT/DATA events\n if (patched.payload !== event.payload && patched.id >= TEXT) {\n const headerWriter = new BinaryWriter();\n headerWriter.writeU8(patched.id);\n headerWriter.writeVarInt(patched.payload.length);\n return {\n ...patched,\n header: headerWriter.toBuffer(),\n };\n }\n\n return patched;\n });\n\n return {\n ...parsed,\n events: patchedEvents,\n };\n}\n\n/**\n * Finds all events with a specific ID\n */\nexport function findEvents(parsed: ParsedFlp, eventId: number): FlpEvent[] {\n return parsed.events.filter((e) => e.id === eventId);\n}\n\n/**\n * Finds the first event with a specific ID\n */\nexport function findFirstEvent(parsed: ParsedFlp, eventId: number): FlpEvent | undefined {\n return parsed.events.find((e) => e.id === eventId);\n}\n\n/**\n * Gets event payload as a string (handles encoding)\n */\nexport function getEventString(event: FlpEvent, useUnicode: boolean): string {\n if (event.kind !== \"text\") {\n throw new Error(`Event ${event.id} is not a text event`);\n }\n\n if (useUnicode) {\n const str = event.payload.toString(\"utf16le\");\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n } else {\n const str = event.payload.toString(\"ascii\");\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n}\n\n/**\n * Creates a text payload with proper encoding\n */\nexport function createTextPayload(text: string, useUnicode: boolean): Buffer {\n if (useUnicode) {\n return Buffer.from(text + \"\\0\", \"utf16le\");\n } else {\n return Buffer.from(text + \"\\0\", \"ascii\");\n }\n}\n\n/**\n * Gets event payload as a number\n */\nexport function getEventNumber(event: FlpEvent): number {\n const reader = new BinaryReader(event.payload);\n\n switch (event.kind) {\n case \"u8\":\n return reader.readU8();\n case \"i8\":\n return reader.readI8();\n case \"u16\":\n return reader.readU16LE();\n case \"i16\":\n return reader.readI16LE();\n case \"u32\":\n return reader.readU32LE();\n case \"i32\":\n return reader.readI32LE();\n case \"f32\":\n return reader.readF32LE();\n default:\n throw new Error(`Cannot get number from ${event.kind} event`);\n }\n}\n\n/**\n * Creates a numeric payload\n */\nexport function createNumberPayload(\n value: number,\n kind: \"u8\" | \"i8\" | \"u16\" | \"i16\" | \"u32\" | \"i32\" | \"f32\"\n): Buffer {\n const writer = new BinaryWriter();\n\n switch (kind) {\n case \"u8\":\n writer.writeU8(value);\n break;\n case \"i8\":\n writer.writeI8(value);\n break;\n case \"u16\":\n writer.writeU16LE(value);\n break;\n case \"i16\":\n writer.writeI16LE(value);\n break;\n case \"u32\":\n writer.writeU32LE(value);\n break;\n case \"i32\":\n writer.writeI32LE(value);\n break;\n case \"f32\":\n writer.writeF32LE(value);\n break;\n }\n\n return writer.toBuffer();\n}\n","/**\n * High-level API for reading and modifying FL Studio project files\n * Provides typed access to project metadata, samples, plugins, and time info\n */\n\nimport { EVENT_ID } from \"../generated/events.generated.js\";\nimport { BinaryReader } from \"../io/BinaryReader.js\";\nimport { BinaryWriter } from \"../io/BinaryWriter.js\";\nimport {\n createNumberPayload,\n createTextPayload,\n findFirstEvent,\n type FlpEvent,\n getEventNumber,\n getEventString,\n type ParsedFlp,\n patchEvents,\n} from \"../parser/FlpParser.js\";\n\n// ============================================================================\n// Project Metadata\n// ============================================================================\n\n/**\n * Project metadata (name, description, artist, genre, BPM)\n */\nexport interface ProjectMeta {\n name: string | null;\n description: string | null;\n artist: string | null;\n genre: string | null;\n bpm: number | null;\n}\n\n/**\n * Reads project metadata from a parsed FLP\n */\nexport function readProjectMeta(parsed: ParsedFlp): ProjectMeta {\n const titleEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TITLE);\n const commentsEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_COMMENTS);\n const artistsEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_ARTISTS);\n const genreEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_GENRE);\n const tempoEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TEMPO);\n\n return {\n name: titleEvent ? getEventString(titleEvent, parsed.useUnicode) : null,\n description: commentsEvent ? getEventString(commentsEvent, parsed.useUnicode) : null,\n artist: artistsEvent ? getEventString(artistsEvent, parsed.useUnicode) : null,\n genre: genreEvent ? getEventString(genreEvent, parsed.useUnicode) : null,\n bpm: tempoEvent ? getEventNumber(tempoEvent) / 1000 : null,\n };\n}\n\n/**\n * Writes project metadata to a parsed FLP\n * Only modifies fields that are provided (non-undefined)\n */\nexport function writeProjectMeta(parsed: ParsedFlp, meta: Partial<ProjectMeta>): ParsedFlp {\n return patchEvents(parsed, (event: FlpEvent) => {\n // Patch title\n if (meta.name !== undefined && event.id === EVENT_ID.PROJECT_TITLE) {\n return {\n ...event,\n payload: createTextPayload(meta.name ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch comments/description\n if (meta.description !== undefined && event.id === EVENT_ID.PROJECT_COMMENTS) {\n return {\n ...event,\n payload: createTextPayload(meta.description ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch artists\n if (meta.artist !== undefined && event.id === EVENT_ID.PROJECT_ARTISTS) {\n return {\n ...event,\n payload: createTextPayload(meta.artist ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch genre\n if (meta.genre !== undefined && event.id === EVENT_ID.PROJECT_GENRE) {\n return {\n ...event,\n payload: createTextPayload(meta.genre ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch tempo (stored as BPM * 1000)\n if (meta.bpm && Math.abs(meta.bpm) > 0 && event.id === EVENT_ID.PROJECT_TEMPO) {\n return {\n ...event,\n payload: createNumberPayload(Math.round(Math.abs(meta.bpm) * 1000), \"u32\"),\n };\n }\n\n return event;\n });\n}\n\n// ============================================================================\n// Project Time Info\n// ============================================================================\n\n/**\n * Project creation date and work time\n */\nexport interface ProjectTimeInfo {\n creationDate: Date | null;\n workTimeSeconds: number | null;\n}\n\n// Delphi epoch: December 30, 1899\nconst DELPHI_EPOCH = new Date(1899, 11, 30);\n\n/**\n * Converts Delphi timestamp (days since epoch) to Date\n */\nfunction delphiToDate(days: number): Date {\n const ms = days * 24 * 60 * 60 * 1000;\n return new Date(DELPHI_EPOCH.getTime() + ms);\n}\n\n/**\n * Converts Date to Delphi timestamp (days since epoch)\n */\nfunction dateToDelphiDays(date: Date): number {\n const ms = date.getTime() - DELPHI_EPOCH.getTime();\n return ms / (24 * 60 * 60 * 1000);\n}\n\n/**\n * Reads project time info from a parsed FLP\n * Timestamp event contains: Float64 created_on (days), Float64 time_spent (days)\n */\nexport function readProjectTimeInfo(parsed: ParsedFlp): ProjectTimeInfo {\n const timestampEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TIMESTAMP);\n\n if (!timestampEvent || timestampEvent.payload.length < 16) {\n return {\n creationDate: null,\n workTimeSeconds: null,\n };\n }\n\n const reader = new BinaryReader(timestampEvent.payload);\n const createdOnDays = reader.readF64LE();\n const timeSpentDays = reader.readF64LE();\n\n return {\n creationDate: delphiToDate(createdOnDays),\n workTimeSeconds: timeSpentDays * 24 * 60 * 60, // Convert days to seconds\n };\n}\n\n/**\n * Writes project time info to a parsed FLP\n * Only modifies fields that are provided (non-undefined)\n * \n * If `creationDate` or `workTimeSeconds` is set to `null`, the final value will be set on `0` (Delphi epoch)\n */\nexport function writeProjectTimeInfo(\n parsed: ParsedFlp,\n info: Partial<ProjectTimeInfo>\n): ParsedFlp {\n // Read current values to preserve unmodified fields\n const current = readProjectTimeInfo(parsed);\n\n return patchEvents(parsed, (event: FlpEvent) => {\n if (event.id !== EVENT_ID.PROJECT_TIMESTAMP) {\n return event;\n }\n\n const writer = new BinaryWriter();\n\n // Write creation date\n if (info.creationDate !== undefined && info.creationDate !== null) {\n writer.writeF64LE(dateToDelphiDays(info.creationDate));\n } else if (current.creationDate !== null) {\n writer.writeF64LE(dateToDelphiDays(current.creationDate));\n } else {\n writer.writeF64LE(0);\n }\n\n // Write work time\n if (info.workTimeSeconds !== undefined && info.workTimeSeconds !== null) {\n writer.writeF64LE(info.workTimeSeconds / (24 * 60 * 60)); // Convert seconds to days\n } else if (current.workTimeSeconds !== null) {\n writer.writeF64LE(current.workTimeSeconds / (24 * 60 * 60));\n } else {\n writer.writeF64LE(0);\n }\n\n return {\n ...event,\n payload: writer.toBuffer(),\n };\n });\n}\n\n// ============================================================================\n// Samples\n// ============================================================================\n\n/**\n * Reference to a sample in the project\n */\nexport interface SampleRef {\n /** Index of the event in the events array */\n eventIndex: number;\n /** File path of the sample */\n path: string;\n}\n\n/**\n * Lists all samples in the project\n */\nexport function listSamples(parsed: ParsedFlp): SampleRef[] {\n const samples: SampleRef[] = [];\n\n parsed.events.forEach((event: FlpEvent, index: number) => {\n if (event.id === EVENT_ID.CHANNEL_SAMPLE_PATH) {\n const path = getEventString(event, parsed.useUnicode);\n if (path && path.length > 0) {\n samples.push({\n eventIndex: index,\n path,\n });\n }\n }\n });\n\n return samples;\n}\n\n/**\n * Rewrites sample paths using a mapper function\n */\nexport function rewriteSamplePaths(\n parsed: ParsedFlp,\n mapper: (oldPath: string) => string\n): ParsedFlp {\n return patchEvents(parsed, (event: FlpEvent) => {\n if (event.id !== EVENT_ID.CHANNEL_SAMPLE_PATH) {\n return event;\n }\n\n const oldPath = getEventString(event, parsed.useUnicode);\n if (!oldPath || oldPath.length === 0) {\n return event;\n }\n\n const newPath = mapper(oldPath);\n if (newPath === oldPath) {\n return event;\n }\n\n return {\n ...event,\n payload: createTextPayload(newPath, parsed.useUnicode),\n };\n });\n}\n\n// ============================================================================\n// Plugins (VST)\n// ============================================================================\n\n/**\n * Reference to a plugin in the project\n */\nexport interface PluginRef {\n /** Plugin name (may be null if not available) */\n name: string | null;\n /** Plugin vendor/provider (may be null if not stored in file) */\n vendor: string | null;\n}\n\n/**\n * VST Plugin Event internal IDs (from plugin.py VSTPluginEvent)\n */\nconst VST_EVENT_ID = {\n MIDI: 1,\n FLAGS: 2,\n IO: 30,\n INPUTS: 31,\n OUTPUTS: 32,\n PLUGIN_INFO: 50,\n FOUR_CC: 51,\n GUID: 52,\n STATE: 53,\n NAME: 54,\n PLUGIN_PATH: 55,\n VENDOR: 56,\n} as const;\n\n/**\n * Parses a VST plugin data event to extract name and vendor\n * Based on VSTPluginEvent structure in PyFLP\n */\nfunction parseVstPluginData(payload: Buffer): { name: string | null; vendor: string | null } {\n if (payload.length < 4) {\n return { name: null, vendor: null };\n }\n\n const reader = new BinaryReader(payload);\n\n // First 4 bytes: type marker (8, 10, or 11 for VSTs)\n const typeMarker = reader.readU32LE();\n\n // Skip if not a VST plugin marker\n if (typeMarker !== 8 && typeMarker !== 10 && typeMarker !== 11) {\n return { name: null, vendor: null };\n }\n\n let name: string | null = null;\n let vendor: string | null = null;\n\n // Parse sub-events\n while (reader.remaining() >= 12) {\n // At minimum: 4 (id) + 8 (size)\n const subEventId = reader.readU32LE();\n const dataSize = reader.readU32LE(); // Lower 32 bits of size\n const dataSizeHigh = reader.readU32LE(); // Upper 32 bits (usually 0)\n\n // Combine for 64-bit size (though in practice it's always small)\n const actualSize = dataSize + dataSizeHigh * 0x100000000;\n\n if (actualSize > reader.remaining()) {\n break;\n }\n\n const data = reader.readBytes(actualSize);\n\n // Extract name and vendor\n if (subEventId === VST_EVENT_ID.NAME && actualSize > 0) {\n name = data.toString(\"utf8\").replace(/\\0/g, \"\");\n } else if (subEventId === VST_EVENT_ID.VENDOR && actualSize > 0) {\n vendor = data.toString(\"utf8\").replace(/\\0/g, \"\");\n }\n }\n\n return { name, vendor };\n}\n\n/**\n * Lists all plugins (VST and native) in the project\n * Returns plugin name and vendor where available\n */\nexport function listPlugins(parsed: ParsedFlp): PluginRef[] {\n const plugins: PluginRef[] = [];\n const seenPlugins = new Set<string>();\n\n // Track current channel context\n let currentInternalName: string | null = null;\n let currentPluginName: string | null = null;\n\n for (const event of parsed.events) {\n // Track channel new events as boundaries\n if (event.id === EVENT_ID.CHANNEL_NEW) {\n // Reset context for new channel\n if (currentInternalName || currentPluginName) {\n // Save previous channel's plugin if it wasn't a VST (no plugin data)\n const key = `${currentInternalName ?? \"\"}:${currentPluginName ?? \"\"}`;\n if (!seenPlugins.has(key) && currentInternalName && currentInternalName !== \"Fruity Wrapper\") {\n seenPlugins.add(key);\n plugins.push({\n name: currentPluginName || currentInternalName,\n vendor: null, // Native plugins don't store vendor\n });\n }\n }\n currentInternalName = null;\n currentPluginName = null;\n }\n\n // Track internal name (identifies plugin type)\n if (event.id === EVENT_ID.PLUGIN_INTERNAL_NAME) {\n currentInternalName = getEventString(event, parsed.useUnicode);\n }\n\n // Track display name\n if (event.id === EVENT_ID.PLUGIN_NAME) {\n currentPluginName = getEventString(event, parsed.useUnicode);\n }\n\n // Parse VST plugin data\n if (event.id === EVENT_ID.PLUGIN_DATA && currentInternalName === \"Fruity Wrapper\") {\n const vstData = parseVstPluginData(event.payload);\n if (vstData.name) {\n const key = `vst:${vstData.name}:${vstData.vendor ?? \"\"}`;\n if (!seenPlugins.has(key)) {\n seenPlugins.add(key);\n plugins.push({\n name: vstData.name,\n vendor: vstData.vendor,\n });\n }\n }\n // Reset context after processing VST\n currentInternalName = null;\n currentPluginName = null;\n }\n }\n\n // Handle last channel if any\n if (currentInternalName && currentInternalName !== \"Fruity Wrapper\") {\n const key = `${currentInternalName}:${currentPluginName ?? \"\"}`;\n if (!seenPlugins.has(key)) {\n plugins.push({\n name: currentPluginName || currentInternalName,\n vendor: null,\n });\n }\n }\n\n return plugins;\n}\n\n// ============================================================================\n// FL Version\n// ============================================================================\n\n/**\n * Gets the FL Studio version string from the project\n */\nexport function getFlVersion(parsed: ParsedFlp): string {\n return parsed.flVersion;\n}\n\n/**\n * Gets the PPQ (Pulses Per Quarter note) from the header\n */\nexport function getPPQ(parsed: ParsedFlp): number {\n // PPQ is at offset 12-13 in the header (bytes 12-13 of the 14-byte FLhd chunk)\n const reader = new BinaryReader(parsed.headerChunkBytes);\n reader.seek(12);\n return reader.readU16LE();\n}\n"],"mappings":"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","names":["SmartBuffer","protobuf","headerSize"]}
1
+ {"version":3,"sources":["../src/generated/events.generated.ts","../src/io/BinaryReader.ts","../src/io/BinaryWriter.ts","../src/parser/FlpParser.ts","../src/api/ProjectApi.ts"],"sourcesContent":["/**\r\n * Auto-generated event constants from PyFLP\r\n * FL Studio .flp file event IDs and types\r\n *\r\n * Event ID ranges:\r\n * - BYTE (0-63): 1 byte data\r\n * - WORD (64-127): 2 bytes data\r\n * - DWORD (128-191): 4 bytes data\r\n * - TEXT (192-207): Variable length strings (+ some IDs in DATA range)\r\n * - DATA (208+): Variable length binary data\r\n */\r\n\r\n// Base offsets for event ID ranges\r\nexport const BYTE = 0;\r\nexport const WORD = 64;\r\nexport const DWORD = 128;\r\nexport const TEXT = 192;\r\nexport const DATA = 208;\r\n\r\n// IDs for TEXT events that use UTF-16LE in newer FL versions\r\nexport const NEW_TEXT_IDS = [\r\n TEXT + 49, // ArrangementID.Name\r\n TEXT + 39, // DisplayGroupID.Name\r\n TEXT + 47, // TrackID.Name\r\n] as const;\r\n\r\n/**\r\n * Event IDs used by FL Studio project files\r\n * Based on PyFLP's event definitions\r\n */\r\nexport const EVENT_ID = {\r\n // === Project IDs (from project.py) ===\r\n PROJECT_LOOP_ACTIVE: 9,\r\n PROJECT_SHOW_INFO: 10,\r\n PROJECT__VOLUME: 12,\r\n PROJECT_PAN_LAW: 23,\r\n PROJECT_LICENSED: 28,\r\n PROJECT__TEMPO_COARSE: WORD + 2, // 66\r\n PROJECT_PITCH: WORD + 16, // 80\r\n PROJECT__TEMPO_FINE: WORD + 29, // 93\r\n PROJECT_CUR_GROUP_ID: DWORD + 18,\r\n PROJECT_TEMPO: DWORD + 28,\r\n PROJECT_FL_BUILD: DWORD + 31,\r\n PROJECT_TITLE: TEXT + 2, // 194\r\n PROJECT_COMMENTS: TEXT + 3, // 195\r\n PROJECT_URL: TEXT + 5, // 197\r\n PROJECT__RTF_COMMENTS: TEXT + 6, // 198\r\n PROJECT_FL_VERSION: TEXT + 7, // 199\r\n PROJECT_LICENSEE: TEXT + 8, // 200\r\n PROJECT_DATA_PATH: TEXT + 10, // 202\r\n PROJECT_GENRE: TEXT + 14, // 206\r\n PROJECT_ARTISTS: TEXT + 15, // 207\r\n PROJECT_TIMESTAMP: DATA + 29, // 237\r\n\r\n // === Channel IDs (from channel.py) ===\r\n CHANNEL_IS_ENABLED: 0,\r\n CHANNEL__VOL_BYTE: 2,\r\n CHANNEL__PAN_BYTE: 3,\r\n CHANNEL_ZIPPED: 15,\r\n CHANNEL_PING_PONG_LOOP: 20,\r\n CHANNEL_TYPE: 21,\r\n CHANNEL_ROUTED_TO: 22,\r\n CHANNEL_IS_LOCKED: 32,\r\n CHANNEL_NEW: WORD,\r\n CHANNEL_FREQ_TILT: WORD + 5, // 69\r\n CHANNEL_FX_FLAGS: WORD + 6, // 70\r\n CHANNEL_CUTOFF: WORD + 7, // 71\r\n CHANNEL__VOL_WORD: WORD + 8, // 72\r\n CHANNEL__PAN_WORD: WORD + 9, // 73\r\n CHANNEL_PREAMP: WORD + 10, // 74\r\n CHANNEL_FADE_OUT: WORD + 11, // 75\r\n CHANNEL_FADE_IN: WORD + 12, // 76\r\n CHANNEL_RESONANCE: WORD + 19, // 83\r\n CHANNEL_STEREO_DELAY: WORD + 21, // 85\r\n CHANNEL_POGO: WORD + 22, // 86\r\n CHANNEL_TIME_SHIFT: WORD + 25, // 89\r\n CHANNEL_CHILDREN: WORD + 30, // 94\r\n CHANNEL_SWING: WORD + 33, // 97\r\n CHANNEL_RING_MOD: DWORD + 3,\r\n CHANNEL_CUT_GROUP: DWORD + 4,\r\n CHANNEL_ROOT_NOTE: DWORD + 7,\r\n CHANNEL_DELAY_MOD_XY: DWORD + 10,\r\n CHANNEL_REVERB: DWORD + 11,\r\n CHANNEL__STRETCH_TIME: DWORD + 12,\r\n CHANNEL_FINE_TUNE: DWORD + 14,\r\n CHANNEL_SAMPLER_FLAGS: DWORD + 15,\r\n CHANNEL_LAYER_FLAGS: DWORD + 16,\r\n CHANNEL_GROUP_NUM: DWORD + 17,\r\n CHANNEL_AU_SAMPLE_RATE: DWORD + 25,\r\n CHANNEL__NAME: TEXT,\r\n CHANNEL_SAMPLE_PATH: TEXT + 4, // 196\r\n CHANNEL_DELAY: DATA + 1, // 209\r\n CHANNEL_PARAMETERS: DATA + 7, // 215\r\n CHANNEL_ENVELOPE_LFO: DATA + 10, // 218\r\n CHANNEL_LEVELS: DATA + 11, // 219\r\n CHANNEL_POLYPHONY: DATA + 13, // 221\r\n CHANNEL_TRACKING: DATA + 20, // 228\r\n CHANNEL_LEVEL_ADJUSTS: DATA + 21, // 229\r\n CHANNEL_AUTOMATION: DATA + 26, // 234\r\n\r\n // === Plugin IDs (from plugin.py) ===\r\n PLUGIN_COLOR: DWORD,\r\n PLUGIN_ICON: DWORD + 27,\r\n PLUGIN_INTERNAL_NAME: TEXT + 9, // 201\r\n PLUGIN_NAME: TEXT + 11, // 203\r\n PLUGIN_WRAPPER: DATA + 4, // 212\r\n PLUGIN_DATA: DATA + 5, // 213\r\n\r\n // === Display Group IDs ===\r\n DISPLAY_GROUP_NAME: TEXT + 39, // 231\r\n\r\n // === Rack IDs ===\r\n RACK_SWING: 11,\r\n RACK__FIT_TO_STEPS: 13,\r\n RACK_WINDOW_HEIGHT: DWORD + 5,\r\n\r\n // === Mixer IDs (from mixer.py) ===\r\n MIXER_SLOT_INDEX: WORD + 34, // 98\r\n MIXER_INSERT_ICON: WORD + 31, // 95\r\n MIXER_INSERT_OUTPUT: DWORD + 19,\r\n MIXER_INSERT_COLOR: DWORD + 21,\r\n MIXER_INSERT_INPUT: DWORD + 26,\r\n MIXER_INSERT_NAME: TEXT + 12, // 204\r\n MIXER_INSERT_ROUTING: DATA + 27, // 235\r\n MIXER_INSERT_FLAGS: DATA + 28, // 236\r\n} as const;\r\n\r\n/**\r\n * Event data type classification\r\n */\r\nexport type EventKind =\r\n | \"u8\"\r\n | \"i8\"\r\n | \"u16\"\r\n | \"i16\"\r\n | \"u32\"\r\n | \"i32\"\r\n | \"f32\"\r\n | \"text\"\r\n | \"data\"\r\n | \"unknown\";\r\n\r\n/**\r\n * Maps event IDs to their data type\r\n * Used for proper serialization/deserialization\r\n */\r\nexport const EVENT_KIND: Record<number, EventKind> = {\r\n // Project events\r\n [EVENT_ID.PROJECT_LOOP_ACTIVE]: \"u8\",\r\n [EVENT_ID.PROJECT_SHOW_INFO]: \"u8\",\r\n [EVENT_ID.PROJECT__VOLUME]: \"u8\",\r\n [EVENT_ID.PROJECT_PAN_LAW]: \"u8\",\r\n [EVENT_ID.PROJECT_LICENSED]: \"u8\",\r\n [EVENT_ID.PROJECT__TEMPO_COARSE]: \"u16\",\r\n [EVENT_ID.PROJECT_PITCH]: \"i16\",\r\n [EVENT_ID.PROJECT__TEMPO_FINE]: \"u16\",\r\n [EVENT_ID.PROJECT_CUR_GROUP_ID]: \"i32\",\r\n [EVENT_ID.PROJECT_TEMPO]: \"u32\",\r\n [EVENT_ID.PROJECT_FL_BUILD]: \"u32\",\r\n [EVENT_ID.PROJECT_TITLE]: \"text\",\r\n [EVENT_ID.PROJECT_COMMENTS]: \"text\",\r\n [EVENT_ID.PROJECT_URL]: \"text\",\r\n [EVENT_ID.PROJECT__RTF_COMMENTS]: \"text\",\r\n [EVENT_ID.PROJECT_FL_VERSION]: \"text\",\r\n [EVENT_ID.PROJECT_LICENSEE]: \"text\",\r\n [EVENT_ID.PROJECT_DATA_PATH]: \"text\",\r\n [EVENT_ID.PROJECT_GENRE]: \"text\",\r\n [EVENT_ID.PROJECT_ARTISTS]: \"text\",\r\n [EVENT_ID.PROJECT_TIMESTAMP]: \"data\",\r\n\r\n // Channel events\r\n [EVENT_ID.CHANNEL_IS_ENABLED]: \"u8\",\r\n [EVENT_ID.CHANNEL__VOL_BYTE]: \"u8\",\r\n [EVENT_ID.CHANNEL__PAN_BYTE]: \"u8\",\r\n [EVENT_ID.CHANNEL_ZIPPED]: \"u8\",\r\n [EVENT_ID.CHANNEL_PING_PONG_LOOP]: \"u8\",\r\n [EVENT_ID.CHANNEL_TYPE]: \"u8\",\r\n [EVENT_ID.CHANNEL_ROUTED_TO]: \"i8\",\r\n [EVENT_ID.CHANNEL_IS_LOCKED]: \"u8\",\r\n [EVENT_ID.CHANNEL_NEW]: \"u16\",\r\n [EVENT_ID.CHANNEL_FREQ_TILT]: \"u16\",\r\n [EVENT_ID.CHANNEL_FX_FLAGS]: \"u16\",\r\n [EVENT_ID.CHANNEL_CUTOFF]: \"u16\",\r\n [EVENT_ID.CHANNEL__VOL_WORD]: \"u16\",\r\n [EVENT_ID.CHANNEL__PAN_WORD]: \"u16\",\r\n [EVENT_ID.CHANNEL_PREAMP]: \"u16\",\r\n [EVENT_ID.CHANNEL_FADE_OUT]: \"u16\",\r\n [EVENT_ID.CHANNEL_FADE_IN]: \"u16\",\r\n [EVENT_ID.CHANNEL_RESONANCE]: \"u16\",\r\n [EVENT_ID.CHANNEL_STEREO_DELAY]: \"u16\",\r\n [EVENT_ID.CHANNEL_POGO]: \"u16\",\r\n [EVENT_ID.CHANNEL_TIME_SHIFT]: \"u16\",\r\n [EVENT_ID.CHANNEL_CHILDREN]: \"u16\",\r\n [EVENT_ID.CHANNEL_SWING]: \"u16\",\r\n [EVENT_ID.CHANNEL_RING_MOD]: \"u32\",\r\n [EVENT_ID.CHANNEL_CUT_GROUP]: \"u32\",\r\n [EVENT_ID.CHANNEL_ROOT_NOTE]: \"u32\",\r\n [EVENT_ID.CHANNEL_DELAY_MOD_XY]: \"u32\",\r\n [EVENT_ID.CHANNEL_REVERB]: \"u32\",\r\n [EVENT_ID.CHANNEL__STRETCH_TIME]: \"f32\",\r\n [EVENT_ID.CHANNEL_FINE_TUNE]: \"i32\",\r\n [EVENT_ID.CHANNEL_SAMPLER_FLAGS]: \"u32\",\r\n [EVENT_ID.CHANNEL_LAYER_FLAGS]: \"u32\",\r\n [EVENT_ID.CHANNEL_GROUP_NUM]: \"i32\",\r\n [EVENT_ID.CHANNEL_AU_SAMPLE_RATE]: \"u32\",\r\n [EVENT_ID.CHANNEL__NAME]: \"text\",\r\n [EVENT_ID.CHANNEL_SAMPLE_PATH]: \"text\",\r\n [EVENT_ID.CHANNEL_DELAY]: \"data\",\r\n [EVENT_ID.CHANNEL_PARAMETERS]: \"data\",\r\n [EVENT_ID.CHANNEL_ENVELOPE_LFO]: \"data\",\r\n [EVENT_ID.CHANNEL_LEVELS]: \"data\",\r\n [EVENT_ID.CHANNEL_POLYPHONY]: \"data\",\r\n [EVENT_ID.CHANNEL_TRACKING]: \"data\",\r\n [EVENT_ID.CHANNEL_LEVEL_ADJUSTS]: \"data\",\r\n [EVENT_ID.CHANNEL_AUTOMATION]: \"data\",\r\n\r\n // Plugin events\r\n [EVENT_ID.PLUGIN_COLOR]: \"u32\",\r\n [EVENT_ID.PLUGIN_ICON]: \"u32\",\r\n [EVENT_ID.PLUGIN_INTERNAL_NAME]: \"text\",\r\n [EVENT_ID.PLUGIN_NAME]: \"text\",\r\n [EVENT_ID.PLUGIN_WRAPPER]: \"data\",\r\n [EVENT_ID.PLUGIN_DATA]: \"data\",\r\n\r\n // Display Group events\r\n [EVENT_ID.DISPLAY_GROUP_NAME]: \"data\",\r\n\r\n // Rack events\r\n [EVENT_ID.RACK_SWING]: \"u8\",\r\n [EVENT_ID.RACK__FIT_TO_STEPS]: \"u8\",\r\n [EVENT_ID.RACK_WINDOW_HEIGHT]: \"u32\",\r\n\r\n // Mixer events\r\n [EVENT_ID.MIXER_SLOT_INDEX]: \"u16\",\r\n [EVENT_ID.MIXER_INSERT_ICON]: \"i16\",\r\n [EVENT_ID.MIXER_INSERT_OUTPUT]: \"i32\",\r\n [EVENT_ID.MIXER_INSERT_COLOR]: \"u32\",\r\n [EVENT_ID.MIXER_INSERT_INPUT]: \"i32\",\r\n [EVENT_ID.MIXER_INSERT_NAME]: \"text\",\r\n [EVENT_ID.MIXER_INSERT_ROUTING]: \"data\",\r\n [EVENT_ID.MIXER_INSERT_FLAGS]: \"data\",\r\n\r\n};\r\n\r\n/**\r\n * Determines the event kind based on ID\r\n * Falls back to range-based detection if not explicitly mapped\r\n */\r\nexport function getEventKind(eventId: number): EventKind {\r\n // Check explicit mapping first\r\n const mapped = EVENT_KIND[eventId];\r\n if (mapped !== undefined) {\r\n return mapped;\r\n }\r\n\r\n // Fall back to range-based detection\r\n if (eventId < WORD) {\r\n return \"u8\";\r\n } else if (eventId < DWORD) {\r\n return \"u16\";\r\n } else if (eventId < TEXT) {\r\n return \"u32\";\r\n } else if (eventId < DATA || NEW_TEXT_IDS.includes(eventId as 241 | 231 | 239)) {\r\n return \"text\";\r\n } else {\r\n return \"data\";\r\n }\r\n}\r\n\r\n/**\r\n * Returns the fixed size for BYTE/WORD/DWORD events\r\n * Returns -1 for variable-length events (TEXT/DATA)\r\n */\r\nexport function getEventFixedSize(eventId: number): number {\r\n if (eventId < WORD) {\r\n return 1;\r\n } else if (eventId < DWORD) {\r\n return 2;\r\n } else if (eventId < TEXT) {\r\n return 4;\r\n }\r\n return -1; // Variable length\r\n}\r\n","/**\n * Binary reader for FL Studio .flp files\n * Uses smart-buffer for binary reading and protobufjs for VarInt decoding\n */\n\nimport protobuf from \"protobufjs/minimal.js\";\nimport { SmartBuffer } from \"smart-buffer\";\n\nexport class BinaryReader {\n private sb: SmartBuffer;\n private source: Buffer;\n\n constructor(buffer: Buffer) {\n this.source = buffer;\n this.sb = SmartBuffer.fromBuffer(buffer);\n }\n\n /**\n * Returns current read position\n */\n tell(): number {\n return this.sb.readOffset;\n }\n\n /**\n * Seeks to an absolute position\n */\n seek(pos: number): void {\n this.sb.readOffset = pos;\n }\n\n /**\n * Seeks relative to current position\n */\n skip(offset: number): void {\n this.sb.readOffset += offset;\n }\n\n /**\n * Returns total buffer length\n */\n length(): number {\n return this.sb.length;\n }\n\n /**\n * Returns remaining bytes to read\n */\n remaining(): number {\n return this.sb.remaining();\n }\n\n /**\n * Returns true if there are more bytes to read\n */\n hasMore(): boolean {\n return this.sb.remaining() > 0;\n }\n\n /**\n * Reads an unsigned 8-bit integer\n */\n readU8(): number {\n return this.sb.readUInt8();\n }\n\n /**\n * Reads a signed 8-bit integer\n */\n readI8(): number {\n return this.sb.readInt8();\n }\n\n /**\n * Reads an unsigned 16-bit integer (little-endian)\n */\n readU16LE(): number {\n return this.sb.readUInt16LE();\n }\n\n /**\n * Reads a signed 16-bit integer (little-endian)\n */\n readI16LE(): number {\n return this.sb.readInt16LE();\n }\n\n /**\n * Reads an unsigned 32-bit integer (little-endian)\n */\n readU32LE(): number {\n return this.sb.readUInt32LE();\n }\n\n /**\n * Reads a signed 32-bit integer (little-endian)\n */\n readI32LE(): number {\n return this.sb.readInt32LE();\n }\n\n /**\n * Reads a 32-bit float (little-endian)\n */\n readF32LE(): number {\n return this.sb.readFloatLE();\n }\n\n /**\n * Reads a 64-bit float (little-endian)\n */\n readF64LE(): number {\n return this.sb.readDoubleLE();\n }\n\n /**\n * Reads raw bytes\n */\n readBytes(length: number): Buffer {\n return this.sb.readBuffer(length);\n }\n\n /**\n * Reads a VarInt (variable-length integer) using protobufjs\n * FL Studio uses this encoding for TEXT and DATA event sizes\n */\n readVarInt(): number {\n // Get remaining buffer from current position\n const remainingBuffer = this.source.subarray(this.sb.readOffset);\n const reader = protobuf.Reader.create(remainingBuffer);\n const value = reader.uint32();\n // Advance our position by how many bytes protobufjs consumed\n this.sb.readOffset += reader.pos;\n return value;\n }\n\n /**\n * Reads an ASCII string (null-terminated)\n */\n readAsciiString(length: number): string {\n const str = this.sb.readString(length, \"ascii\");\n // Remove null terminator if present\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n\n /**\n * Reads a UTF-16LE string (null-terminated)\n */\n readUtf16LEString(length: number): string {\n const bytes = this.sb.readBuffer(length);\n const str = bytes.toString(\"utf16le\");\n // Remove null terminator if present\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n\n /**\n * Peeks at bytes without advancing position\n */\n peek(length: number): Buffer {\n const pos = this.sb.readOffset;\n const data = this.sb.readBuffer(length);\n this.sb.readOffset = pos;\n return data;\n }\n\n /**\n * Peeks at a single byte without advancing position\n */\n peekU8(): number {\n const pos = this.sb.readOffset;\n const value = this.sb.readUInt8();\n this.sb.readOffset = pos;\n return value;\n }\n\n /**\n * Returns the internal buffer\n */\n toBuffer(): Buffer {\n return this.source;\n }\n}\n","/**\n * Binary writer for FL Studio .flp files\n * Uses smart-buffer for binary writing and protobufjs for VarInt encoding\n */\n\nimport { SmartBuffer } from \"smart-buffer\";\nimport protobuf from \"protobufjs/minimal.js\";\n\nexport class BinaryWriter {\n private sb: SmartBuffer;\n\n constructor() {\n this.sb = new SmartBuffer();\n }\n\n /**\n * Returns current write position (total bytes written)\n */\n tell(): number {\n return this.sb.writeOffset;\n }\n\n /**\n * Writes an unsigned 8-bit integer\n */\n writeU8(value: number): void {\n this.sb.writeUInt8(value & 0xff);\n }\n\n /**\n * Writes a signed 8-bit integer\n */\n writeI8(value: number): void {\n this.sb.writeInt8(value);\n }\n\n /**\n * Writes an unsigned 16-bit integer (little-endian)\n */\n writeU16LE(value: number): void {\n this.sb.writeUInt16LE(value & 0xffff);\n }\n\n /**\n * Writes a signed 16-bit integer (little-endian)\n */\n writeI16LE(value: number): void {\n this.sb.writeInt16LE(value);\n }\n\n /**\n * Writes an unsigned 32-bit integer (little-endian)\n */\n writeU32LE(value: number): void {\n this.sb.writeUInt32LE(value >>> 0);\n }\n\n /**\n * Writes a signed 32-bit integer (little-endian)\n */\n writeI32LE(value: number): void {\n this.sb.writeInt32LE(value);\n }\n\n /**\n * Writes a 32-bit float (little-endian)\n */\n writeF32LE(value: number): void {\n this.sb.writeFloatLE(value);\n }\n\n /**\n * Writes a 64-bit float (little-endian)\n */\n writeF64LE(value: number): void {\n this.sb.writeDoubleLE(value);\n }\n\n /**\n * Writes raw bytes\n */\n writeBytes(data: Buffer): void {\n this.sb.writeBuffer(data);\n }\n\n /**\n * Writes a VarInt (variable-length integer) using protobufjs\n * FL Studio uses this encoding for TEXT and DATA event sizes\n */\n writeVarInt(value: number): void {\n const writer = protobuf.Writer.create();\n writer.uint32(value);\n const encoded = writer.finish();\n this.sb.writeBuffer(Buffer.from(encoded));\n }\n\n /**\n * Writes an ASCII string (null-terminated)\n */\n writeAsciiString(str: string): void {\n this.sb.writeString(str + \"\\0\", \"ascii\");\n }\n\n /**\n * Writes a UTF-16LE string (null-terminated)\n */\n writeUtf16LEString(str: string): void {\n const buf = Buffer.from(str + \"\\0\", \"utf16le\");\n this.sb.writeBuffer(buf);\n }\n\n /**\n * Gets the final buffer\n */\n toBuffer(): Buffer {\n return this.sb.toBuffer();\n }\n}\n\n/**\n * Encodes a VarInt to a Buffer using protobufjs (standalone utility)\n */\nexport function encodeVarInt(value: number): Buffer {\n const writer = protobuf.Writer.create();\n writer.uint32(value);\n return Buffer.from(writer.finish());\n}\n\n/**\n * Calculates the byte size needed for a VarInt\n */\nexport function varIntSize(value: number): number {\n if (value < 0x80) return 1;\n if (value < 0x4000) return 2;\n if (value < 0x200000) return 3;\n if (value < 0x10000000) return 4;\n return 5;\n}\n","/**\n * FL Studio .flp file parser and serializer\n * Conservative patch approach: preserves all unknown data byte-for-byte\n */\n\nimport { DWORD, EVENT_ID, TEXT, WORD, getEventKind } from \"../generated/events.generated.js\";\nimport { BinaryReader } from \"../io/BinaryReader.js\";\nimport { BinaryWriter } from \"../io/BinaryWriter.js\";\n\n/**\n * Represents a single event in an FLP file\n * Stores raw bytes to enable conservative patching\n */\nexport interface FlpEvent {\n /** Event ID (0-255) */\n id: number;\n /** Data type classification */\n kind: \"u8\" | \"i8\" | \"u16\" | \"i16\" | \"u32\" | \"i32\" | \"f32\" | \"text\" | \"data\" | \"unknown\";\n /** Original header bytes (ID + size for variable-length events) */\n header: Buffer;\n /** Event payload data */\n payload: Buffer;\n}\n\n/**\n * Represents a parsed FLP file\n * Contains all data needed for conservative round-trip\n */\nexport interface ParsedFlp {\n /** FLhd chunk (header) - 14 bytes total */\n headerChunkBytes: Buffer;\n /** FLdt chunk header (magic + size placeholder) - 8 bytes */\n fldtHeaderBytes: Buffer;\n /** All events from the FLdt chunk */\n events: FlpEvent[];\n /** Any trailing bytes after events (should be empty) */\n trailingBytes: Buffer | undefined;\n /** Detected FL version for string encoding */\n flVersion: string;\n /** Whether to use UTF-16LE for strings (FL >= 11.5) */\n useUnicode: boolean;\n}\n\n/**\n * Header structure constants\n */\nconst FLP_HEADER_MAGIC = \"FLhd\";\nconst FLP_DATA_MAGIC = \"FLdt\";\nconst FLP_HEADER_SIZE = 6; // Expected header chunk size\n\n/**\n * Parses an FL Studio project file\n *\n * @param buffer - Raw file data\n * @returns Parsed FLP structure\n * @throws Error if file is corrupted or invalid\n */\nexport function parseFlp(buffer: Buffer): ParsedFlp {\n const reader = new BinaryReader(buffer);\n\n // Read FLhd header chunk\n const headerMagic = reader.readBytes(4).toString(\"ascii\");\n if (headerMagic !== FLP_HEADER_MAGIC) {\n throw new Error(`Invalid header magic: expected 'FLhd', got '${headerMagic}'`);\n }\n\n const headerSize = reader.readU32LE();\n if (headerSize !== FLP_HEADER_SIZE) {\n throw new Error(`Invalid header size: expected ${FLP_HEADER_SIZE}, got ${headerSize}`);\n }\n\n const format = reader.readI16LE();\n const channelCount = reader.readU16LE();\n const ppq = reader.readU16LE();\n\n // Validate format (0 = normal project)\n if (format < -1 || format > 0x50) {\n throw new Error(`Invalid file format: ${format}`);\n }\n\n // Store complete header chunk (14 bytes)\n const headerChunkBytes = buffer.subarray(0, 14);\n\n // Read FLdt data chunk header\n const dataMagic = reader.readBytes(4).toString(\"ascii\");\n if (dataMagic !== FLP_DATA_MAGIC) {\n throw new Error(`Invalid data magic: expected 'FLdt', got '${dataMagic}'`);\n }\n\n const eventsSize = reader.readU32LE();\n\n // Validate events size\n const expectedFileSize = 22 + eventsSize; // 14 (FLhd) + 8 (FLdt header) + events\n if (buffer.length !== expectedFileSize) {\n throw new Error(\n `Data chunk size mismatch: expected file size ${expectedFileSize}, got ${buffer.length}`\n );\n }\n\n // Store FLdt header (8 bytes)\n const fldtHeaderBytes = buffer.subarray(14, 22);\n\n // Parse events\n const events: FlpEvent[] = [];\n let flVersion = \"0.0.0\";\n let useUnicode = false;\n let detectedVersion = false;\n\n const eventsStart = reader.tell();\n const eventsEnd = eventsStart + eventsSize;\n\n while (reader.tell() < eventsEnd) {\n const eventStart = reader.tell();\n const eventId = reader.readU8();\n\n let payloadSize: number;\n let headerSize: number;\n\n // Determine payload size based on event ID range\n if (eventId < WORD) {\n // BYTE range: 1 byte payload\n payloadSize = 1;\n headerSize = 1; // Just the ID\n } else if (eventId < DWORD) {\n // WORD range: 2 bytes payload\n payloadSize = 2;\n headerSize = 1;\n } else if (eventId < TEXT) {\n // DWORD range: 4 bytes payload\n payloadSize = 4;\n headerSize = 1;\n } else {\n // TEXT/DATA range: VarInt-encoded size\n const sizeStart = reader.tell();\n payloadSize = reader.readVarInt();\n headerSize = 1 + (reader.tell() - sizeStart); // ID + VarInt bytes\n }\n\n // Read payload\n const payload = reader.readBytes(payloadSize);\n\n // Extract header from original buffer for byte-perfect round-trip\n const header = buffer.subarray(eventStart, eventStart + headerSize);\n\n // Determine event kind\n const kind = getEventKind(eventId);\n\n const event: FlpEvent = {\n id: eventId,\n kind,\n header: Buffer.from(header),\n payload: Buffer.from(payload),\n };\n\n events.push(event);\n\n // Detect FL version from FLVersion event\n if (eventId === EVENT_ID.PROJECT_FL_VERSION && !detectedVersion) {\n const candidate = payload.toString(\"ascii\").replace(/\\0/g, \"\").trim();\n const isSemverLike = /^\\d+(\\.\\d+)+$/.test(candidate);\n if (isSemverLike) {\n flVersion = candidate;\n const parts = flVersion.split(\".\").map((p) => parseInt(p, 10));\n const major = parts[0] ?? 0;\n const minor = parts[1] ?? 0;\n useUnicode = parts.length >= 2 && (major > 11 || (major === 11 && minor >= 5));\n detectedVersion = true;\n }\n }\n }\n\n // Check for trailing bytes\n let trailingBytes: Buffer | undefined;\n if (reader.remaining() > 0) {\n trailingBytes = reader.readBytes(reader.remaining());\n }\n\n return {\n headerChunkBytes: Buffer.from(headerChunkBytes),\n fldtHeaderBytes: Buffer.from(fldtHeaderBytes),\n events,\n trailingBytes,\n flVersion,\n useUnicode,\n };\n}\n\n/**\n * Serializes a parsed FLP back to a buffer\n * Recalculates FLdt chunk size if events have changed\n *\n * @param parsed - Parsed FLP structure\n * @returns Serialized buffer\n */\nexport function serializeFlp(parsed: ParsedFlp): Buffer {\n const writer = new BinaryWriter();\n\n // Write FLhd header chunk (preserved byte-for-byte)\n writer.writeBytes(parsed.headerChunkBytes);\n\n // Calculate total events size\n let eventsSize = 0;\n for (const event of parsed.events) {\n eventsSize += serializeEvent(event).length;\n }\n\n // Add trailing bytes if present\n if (parsed.trailingBytes) {\n eventsSize += parsed.trailingBytes.length;\n }\n\n // Write FLdt header with updated size\n writer.writeBytes(Buffer.from(FLP_DATA_MAGIC, \"ascii\"));\n writer.writeU32LE(eventsSize);\n\n // Write all events\n for (const event of parsed.events) {\n writer.writeBytes(serializeEvent(event));\n }\n\n // Write trailing bytes if present\n if (parsed.trailingBytes) {\n writer.writeBytes(parsed.trailingBytes);\n }\n\n return writer.toBuffer();\n}\n\n/**\n * Serializes a single event to bytes\n * Uses original header for unchanged events, recalculates for modified ones\n */\nfunction serializeEvent(event: FlpEvent): Buffer {\n const writer = new BinaryWriter();\n\n // Conservative mode: preserve the original header bytes whenever available.\n // This keeps unchanged files byte-identical, including exact VarInt encoding.\n if (event.header.length > 0) {\n writer.writeBytes(event.header);\n } else {\n writer.writeU8(event.id);\n if (event.id >= TEXT) {\n writer.writeVarInt(event.payload.length);\n }\n }\n\n // Write payload\n writer.writeBytes(event.payload);\n\n return writer.toBuffer();\n}\n\n/**\n * Creates a new event with the given ID and payload\n */\nexport function createEvent(id: number, payload: Buffer): FlpEvent {\n const kind = getEventKind(id);\n\n // Build header\n const headerWriter = new BinaryWriter();\n headerWriter.writeU8(id);\n\n if (id >= TEXT) {\n headerWriter.writeVarInt(payload.length);\n }\n\n return {\n id,\n kind,\n header: headerWriter.toBuffer(),\n payload: Buffer.from(payload),\n };\n}\n\n/**\n * Applies a patcher function to all events\n * Returns a new ParsedFlp with patched events\n *\n * @param parsed - Original parsed FLP\n * @param patcher - Function that transforms each event\n * @returns New ParsedFlp with patched events\n */\nexport function patchEvents(\n parsed: ParsedFlp,\n patcher: (event: FlpEvent, index: number) => FlpEvent\n): ParsedFlp {\n const patchedEvents = parsed.events.map((event, index) => {\n const patched = patcher(event, index);\n\n // If payload changed, recalculate header for TEXT/DATA events\n if (patched.payload !== event.payload && patched.id >= TEXT) {\n const headerWriter = new BinaryWriter();\n headerWriter.writeU8(patched.id);\n headerWriter.writeVarInt(patched.payload.length);\n return {\n ...patched,\n header: headerWriter.toBuffer(),\n };\n }\n\n return patched;\n });\n\n return {\n ...parsed,\n events: patchedEvents,\n };\n}\n\n/**\n * Finds all events with a specific ID\n */\nexport function findEvents(parsed: ParsedFlp, eventId: number): FlpEvent[] {\n return parsed.events.filter((e) => e.id === eventId);\n}\n\n/**\n * Finds the first event with a specific ID\n */\nexport function findFirstEvent(parsed: ParsedFlp, eventId: number): FlpEvent | undefined {\n return parsed.events.find((e) => e.id === eventId);\n}\n\n/**\n * Gets event payload as a string (handles encoding)\n */\nexport function getEventString(event: FlpEvent, useUnicode: boolean): string {\n if (event.kind !== \"text\") {\n throw new Error(`Event ${event.id} is not a text event`);\n }\n\n if (useUnicode) {\n const str = event.payload.toString(\"utf16le\");\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n } else {\n const str = event.payload.toString(\"ascii\");\n const nullPos = str.indexOf(\"\\0\");\n return nullPos === -1 ? str : str.substring(0, nullPos);\n }\n}\n\n/**\n * Creates a text payload with proper encoding\n */\nexport function createTextPayload(text: string, useUnicode: boolean): Buffer {\n if (useUnicode) {\n return Buffer.from(text + \"\\0\", \"utf16le\");\n } else {\n return Buffer.from(text + \"\\0\", \"ascii\");\n }\n}\n\n/**\n * Gets event payload as a number\n */\nexport function getEventNumber(event: FlpEvent): number {\n const reader = new BinaryReader(event.payload);\n\n switch (event.kind) {\n case \"u8\":\n return reader.readU8();\n case \"i8\":\n return reader.readI8();\n case \"u16\":\n return reader.readU16LE();\n case \"i16\":\n return reader.readI16LE();\n case \"u32\":\n return reader.readU32LE();\n case \"i32\":\n return reader.readI32LE();\n case \"f32\":\n return reader.readF32LE();\n default:\n throw new Error(`Cannot get number from ${event.kind} event`);\n }\n}\n\n/**\n * Creates a numeric payload\n */\nexport function createNumberPayload(\n value: number,\n kind: \"u8\" | \"i8\" | \"u16\" | \"i16\" | \"u32\" | \"i32\" | \"f32\"\n): Buffer {\n const writer = new BinaryWriter();\n\n switch (kind) {\n case \"u8\":\n writer.writeU8(value);\n break;\n case \"i8\":\n writer.writeI8(value);\n break;\n case \"u16\":\n writer.writeU16LE(value);\n break;\n case \"i16\":\n writer.writeI16LE(value);\n break;\n case \"u32\":\n writer.writeU32LE(value);\n break;\n case \"i32\":\n writer.writeI32LE(value);\n break;\n case \"f32\":\n writer.writeF32LE(value);\n break;\n }\n\n return writer.toBuffer();\n}\n","/**\n * High-level API for reading and modifying FL Studio project files\n * Provides typed access to project metadata, samples, plugins, and time info\n */\n\nimport { EVENT_ID } from \"../generated/events.generated.js\";\nimport { BinaryReader } from \"../io/BinaryReader.js\";\nimport { BinaryWriter } from \"../io/BinaryWriter.js\";\nimport {\n createNumberPayload,\n createTextPayload,\n findFirstEvent,\n type FlpEvent,\n getEventNumber,\n getEventString,\n type ParsedFlp,\n patchEvents,\n} from \"../parser/FlpParser.js\";\n\n// ============================================================================\n// Project Metadata\n// ============================================================================\n\n/**\n * Project metadata (name, description, artist, genre, BPM)\n */\nexport interface ProjectMeta {\n name: string | null;\n description: string | null;\n artist: string | null;\n genre: string | null;\n bpm: number | null;\n}\n\n/**\n * Reads project metadata from a parsed FLP\n */\nexport function readProjectMeta(parsed: ParsedFlp): ProjectMeta {\n const titleEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TITLE);\n const commentsEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_COMMENTS);\n const artistsEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_ARTISTS);\n const genreEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_GENRE);\n const tempoEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TEMPO);\n\n return {\n name: titleEvent ? getEventString(titleEvent, parsed.useUnicode) : null,\n description: commentsEvent ? getEventString(commentsEvent, parsed.useUnicode) : null,\n artist: artistsEvent ? getEventString(artistsEvent, parsed.useUnicode) : null,\n genre: genreEvent ? getEventString(genreEvent, parsed.useUnicode) : null,\n bpm: tempoEvent ? getEventNumber(tempoEvent) / 1000 : null,\n };\n}\n\n/**\n * Writes project metadata to a parsed FLP\n * Only modifies fields that are provided (non-undefined)\n */\nexport function writeProjectMeta(parsed: ParsedFlp, meta: Partial<ProjectMeta>): ParsedFlp {\n return patchEvents(parsed, (event: FlpEvent) => {\n // Patch title\n if (meta.name !== undefined && event.id === EVENT_ID.PROJECT_TITLE) {\n return {\n ...event,\n payload: createTextPayload(meta.name ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch comments/description\n if (meta.description !== undefined && event.id === EVENT_ID.PROJECT_COMMENTS) {\n return {\n ...event,\n payload: createTextPayload(meta.description ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch artists\n if (meta.artist !== undefined && event.id === EVENT_ID.PROJECT_ARTISTS) {\n return {\n ...event,\n payload: createTextPayload(meta.artist ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch genre\n if (meta.genre !== undefined && event.id === EVENT_ID.PROJECT_GENRE) {\n return {\n ...event,\n payload: createTextPayload(meta.genre ?? \"\", parsed.useUnicode),\n };\n }\n\n // Patch tempo (stored as BPM * 1000)\n if (meta.bpm && Math.abs(meta.bpm) > 0 && event.id === EVENT_ID.PROJECT_TEMPO) {\n return {\n ...event,\n payload: createNumberPayload(Math.round(Math.abs(meta.bpm) * 1000), \"u32\"),\n };\n }\n\n return event;\n });\n}\n\n// ============================================================================\n// Project Time Info\n// ============================================================================\n\n/**\n * Project creation date and work time\n */\nexport interface ProjectTimeInfo {\n creationDate: Date | null;\n workTimeSeconds: number | null;\n}\n\n// Delphi epoch: December 30, 1899\nconst DELPHI_EPOCH = new Date(1899, 11, 30);\n\n/**\n * Converts Delphi timestamp (days since epoch) to Date\n */\nfunction delphiToDate(days: number): Date {\n const ms = days * 24 * 60 * 60 * 1000;\n return new Date(DELPHI_EPOCH.getTime() + ms);\n}\n\n/**\n * Converts Date to Delphi timestamp (days since epoch)\n */\nfunction dateToDelphiDays(date: Date): number {\n const ms = date.getTime() - DELPHI_EPOCH.getTime();\n return ms / (24 * 60 * 60 * 1000);\n}\n\n/**\n * Reads project time info from a parsed FLP\n * Timestamp event contains: Float64 created_on (days), Float64 time_spent (days)\n */\nexport function readProjectTimeInfo(parsed: ParsedFlp): ProjectTimeInfo {\n const timestampEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TIMESTAMP);\n\n if (!timestampEvent || timestampEvent.payload.length < 16) {\n return {\n creationDate: null,\n workTimeSeconds: null,\n };\n }\n\n const reader = new BinaryReader(timestampEvent.payload);\n const createdOnDays = reader.readF64LE();\n const timeSpentDays = reader.readF64LE();\n\n return {\n creationDate: delphiToDate(createdOnDays),\n workTimeSeconds: timeSpentDays * 24 * 60 * 60, // Convert days to seconds\n };\n}\n\n/**\n * Writes project time info to a parsed FLP\n * Only modifies fields that are provided (non-undefined)\n * \n * If `creationDate` or `workTimeSeconds` is set to `null`, the final value will be set on `0` (Delphi epoch)\n */\nexport function writeProjectTimeInfo(\n parsed: ParsedFlp,\n info: Partial<ProjectTimeInfo>\n): ParsedFlp {\n // Read current values to preserve unmodified fields\n const current = readProjectTimeInfo(parsed);\n\n return patchEvents(parsed, (event: FlpEvent) => {\n if (event.id !== EVENT_ID.PROJECT_TIMESTAMP) {\n return event;\n }\n\n const writer = new BinaryWriter();\n\n // Write creation date\n if (info.creationDate !== undefined && info.creationDate !== null) {\n writer.writeF64LE(dateToDelphiDays(info.creationDate));\n } else if (current.creationDate !== null) {\n writer.writeF64LE(dateToDelphiDays(current.creationDate));\n } else {\n writer.writeF64LE(0);\n }\n\n // Write work time\n if (info.workTimeSeconds !== undefined && info.workTimeSeconds !== null) {\n writer.writeF64LE(info.workTimeSeconds / (24 * 60 * 60)); // Convert seconds to days\n } else if (current.workTimeSeconds !== null) {\n writer.writeF64LE(current.workTimeSeconds / (24 * 60 * 60));\n } else {\n writer.writeF64LE(0);\n }\n\n return {\n ...event,\n payload: writer.toBuffer(),\n };\n });\n}\n\n// ============================================================================\n// Samples\n// ============================================================================\n\n/**\n * Reference to a sample in the project\n */\nexport interface SampleRef {\n /** Index of the event in the events array */\n eventIndex: number;\n /** File path of the sample */\n path: string;\n}\n\n/**\n * Lists all samples in the project\n */\nexport function listSamples(parsed: ParsedFlp): SampleRef[] {\n const samples: SampleRef[] = [];\n\n parsed.events.forEach((event: FlpEvent, index: number) => {\n if (event.id === EVENT_ID.CHANNEL_SAMPLE_PATH) {\n const path = getEventString(event, parsed.useUnicode);\n if (path && path.length > 0) {\n samples.push({\n eventIndex: index,\n path,\n });\n }\n }\n });\n\n return samples;\n}\n\n/**\n * Rewrites sample paths using a mapper function\n */\nexport function rewriteSamplePaths(\n parsed: ParsedFlp,\n mapper: (oldPath: string) => string\n): ParsedFlp {\n return patchEvents(parsed, (event: FlpEvent) => {\n if (event.id !== EVENT_ID.CHANNEL_SAMPLE_PATH) {\n return event;\n }\n\n const oldPath = getEventString(event, parsed.useUnicode);\n if (!oldPath || oldPath.length === 0) {\n return event;\n }\n\n const newPath = mapper(oldPath);\n if (newPath === oldPath) {\n return event;\n }\n\n return {\n ...event,\n payload: createTextPayload(newPath, parsed.useUnicode),\n };\n });\n}\n\n// ============================================================================\n// Plugins (VST)\n// ============================================================================\n\n/**\n * Reference to a plugin in the project\n */\nexport interface PluginRef {\n /** Plugin name (may be null if not available) */\n name: string | null;\n /** Plugin vendor/provider (may be null if not stored in file) */\n vendor: string | null;\n}\n\n/**\n * VST Plugin Event internal IDs (from plugin.py VSTPluginEvent)\n */\nconst VST_EVENT_ID = {\n MIDI: 1,\n FLAGS: 2,\n IO: 30,\n INPUTS: 31,\n OUTPUTS: 32,\n PLUGIN_INFO: 50,\n FOUR_CC: 51,\n GUID: 52,\n STATE: 53,\n NAME: 54,\n PLUGIN_PATH: 55,\n VENDOR: 56,\n} as const;\n\n/**\n * Parses a VST plugin data event to extract name and vendor\n * Based on VSTPluginEvent structure in PyFLP\n */\nfunction parseVstPluginData(payload: Buffer): { name: string | null; vendor: string | null } {\n if (payload.length < 4) {\n return { name: null, vendor: null };\n }\n\n const reader = new BinaryReader(payload);\n\n // First 4 bytes: wrapper marker.\n // Historically seen values include 8/10/11, but newer FL versions can use\n // other markers (e.g. 12). We read and ignore the exact value here because\n // this parser is only called for \"Fruity Wrapper\" plugin data.\n const typeMarker = reader.readU32LE();\n void typeMarker;\n\n let name: string | null = null;\n let vendor: string | null = null;\n\n // Parse sub-events\n while (reader.remaining() >= 12) {\n // At minimum: 4 (id) + 8 (size)\n const subEventId = reader.readU32LE();\n const dataSize = reader.readU32LE(); // Lower 32 bits of size\n const dataSizeHigh = reader.readU32LE(); // Upper 32 bits (usually 0)\n\n // Combine for 64-bit size (though in practice it's always small)\n const actualSize = dataSize + dataSizeHigh * 0x100000000;\n\n if (actualSize > reader.remaining()) {\n break;\n }\n\n const data = reader.readBytes(actualSize);\n\n // Extract name and vendor\n if (subEventId === VST_EVENT_ID.NAME && actualSize > 0) {\n name = data.toString(\"utf8\").replace(/\\0/g, \"\");\n } else if (subEventId === VST_EVENT_ID.VENDOR && actualSize > 0) {\n vendor = data.toString(\"utf8\").replace(/\\0/g, \"\");\n }\n }\n\n return { name, vendor };\n}\n\n/**\n * Lists all plugins (VST and native) in the project\n * Returns plugin name and vendor where available\n */\nexport function listPlugins(parsed: ParsedFlp): PluginRef[] {\n const plugins: PluginRef[] = [];\n const seenPlugins = new Set<string>();\n\n // Track current channel context\n let currentInternalName: string | null = null;\n let currentPluginName: string | null = null;\n\n for (const event of parsed.events) {\n // Track channel new events as boundaries\n if (event.id === EVENT_ID.CHANNEL_NEW) {\n // Reset context for new channel\n if (currentInternalName || currentPluginName) {\n // Save previous channel's plugin if it wasn't a VST (no plugin data)\n const key = `${currentInternalName ?? \"\"}:${currentPluginName ?? \"\"}`;\n if (!seenPlugins.has(key) && currentInternalName && currentInternalName !== \"Fruity Wrapper\") {\n seenPlugins.add(key);\n plugins.push({\n name: currentPluginName || currentInternalName,\n vendor: null, // Native plugins don't store vendor\n });\n }\n }\n currentInternalName = null;\n currentPluginName = null;\n }\n\n // Track internal name (identifies plugin type)\n if (event.id === EVENT_ID.PLUGIN_INTERNAL_NAME) {\n currentInternalName = getEventString(event, parsed.useUnicode);\n }\n\n // Track display name\n if (event.id === EVENT_ID.PLUGIN_NAME) {\n currentPluginName = getEventString(event, parsed.useUnicode);\n }\n\n // Parse VST plugin data\n if (event.id === EVENT_ID.PLUGIN_DATA && currentInternalName === \"Fruity Wrapper\") {\n const vstData = parseVstPluginData(event.payload);\n if (vstData.name) {\n const key = `vst:${vstData.name}:${vstData.vendor ?? \"\"}`;\n if (!seenPlugins.has(key)) {\n seenPlugins.add(key);\n plugins.push({\n name: vstData.name,\n vendor: vstData.vendor,\n });\n }\n }\n // Reset context after processing VST\n currentInternalName = null;\n currentPluginName = null;\n }\n }\n\n // Handle last channel if any\n if (currentInternalName && currentInternalName !== \"Fruity Wrapper\") {\n const key = `${currentInternalName}:${currentPluginName ?? \"\"}`;\n if (!seenPlugins.has(key)) {\n plugins.push({\n name: currentPluginName || currentInternalName,\n vendor: null,\n });\n }\n }\n\n return plugins;\n}\n\n// ============================================================================\n// FL Version\n// ============================================================================\n\n/**\n * Gets the FL Studio version string from the project\n */\nexport function getFlVersion(parsed: ParsedFlp): string {\n return parsed.flVersion;\n}\n\n/**\n * Gets the PPQ (Pulses Per Quarter note) from the header\n */\nexport function getPPQ(parsed: ParsedFlp): number {\n // PPQ is at offset 12-13 in the header (bytes 12-13 of the 14-byte FLhd chunk)\n const reader = new BinaryReader(parsed.headerChunkBytes);\n reader.seek(12);\n return reader.readU16LE();\n}\n"],"mappings":"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","names":["SmartBuffer","protobuf","headerSize"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectApi.d.ts","sourceRoot":"","sources":["../../../src/api/ProjectApi.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAOL,KAAK,SAAS,EAEf,MAAM,wBAAwB,CAAC;AAMhC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAc9D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,SAAS,CA4CzF;AAMD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAqBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,eAAe,CAkBtE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,OAAO,CAAC,eAAe,CAAC,GAC7B,SAAS,CAkCX;AAMD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,CAgB1D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAClC,SAAS,CAqBX;AAMD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,iDAAiD;IACjD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,iEAAiE;IACjE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAqED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,CAoE1D;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAKhD"}
1
+ {"version":3,"file":"ProjectApi.d.ts","sourceRoot":"","sources":["../../../src/api/ProjectApi.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAOL,KAAK,SAAS,EAEf,MAAM,wBAAwB,CAAC;AAMhC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAc9D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,SAAS,CA4CzF;AAMD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAqBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,eAAe,CAkBtE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,OAAO,CAAC,eAAe,CAAC,GAC7B,SAAS,CAkCX;AAMD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,CAgB1D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAClC,SAAS,CAqBX;AAMD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,iDAAiD;IACjD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,iEAAiE;IACjE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAoED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,CAoE1D;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAKhD"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export declare class BinaryReader {
6
6
  private sb;
7
+ private source;
7
8
  constructor(buffer: Buffer);
8
9
  /**
9
10
  * Returns current read position
@@ -1 +1 @@
1
- {"version":3,"file":"BinaryReader.d.ts","sourceRoot":"","sources":["../../../src/io/BinaryReader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAc;gBAEZ,MAAM,EAAE,MAAM;IAI1B;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;OAEG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIvB;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAIjC;;;OAGG;IACH,UAAU,IAAI,MAAM;IAUpB;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOvC;;OAEG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAQzC;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAO5B;;OAEG;IACH,MAAM,IAAI,MAAM;IAOhB;;OAEG;IACH,QAAQ,IAAI,MAAM;CAGnB"}
1
+ {"version":3,"file":"BinaryReader.d.ts","sourceRoot":"","sources":["../../../src/io/BinaryReader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAK1B;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;OAEG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIvB;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAIjC;;;OAGG;IACH,UAAU,IAAI,MAAM;IAUpB;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOvC;;OAEG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAQzC;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAO5B;;OAEG;IACH,MAAM,IAAI,MAAM;IAOhB;;OAEG;IACH,QAAQ,IAAI,MAAM;CAGnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"FlpParser.d.ts","sourceRoot":"","sources":["../../../src/parser/FlpParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,+BAA+B;IAC/B,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACxF,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,gBAAgB,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,eAAe,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,wDAAwD;IACxD,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,UAAU,EAAE,OAAO,CAAC;CACrB;AASD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CA0HlD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAgCtD;AAuBD;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAiBjE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,QAAQ,GACpD,SAAS,CAsBX;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAEzE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAEvF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAc3E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAM3E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAqBtD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GACxD,MAAM,CA4BR"}
1
+ {"version":3,"file":"FlpParser.d.ts","sourceRoot":"","sources":["../../../src/parser/FlpParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,+BAA+B;IAC/B,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACxF,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,gBAAgB,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,eAAe,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,wDAAwD;IACxD,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,UAAU,EAAE,OAAO,CAAC;CACrB;AASD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAgIlD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAgCtD;AA0BD;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAiBjE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,QAAQ,GACpD,SAAS,CAsBX;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAEzE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAEvF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAc3E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAM3E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAqBtD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GACxD,MAAM,CA4BR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-flp",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Minimalist TypeScript library for parsing and modifying FL Studio project files (.flp)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",