ts-flp 1.0.5 → 1.0.6

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.
@@ -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 * 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 * Uses look-ahead scoring to handle events in the DWORD range (128-191)\n * that may use either fixed 4-byte encoding or VarInt-encoded variable\n * length payloads. For each unknown event in this range, both\n * interpretations are scored by parsing ahead ~200 bytes and counting\n * how many TEXT/DATA events (with valid VarInt sizes) are reachable.\n * The interpretation producing more valid TEXT/DATA events wins.\n */\n\nimport {\n DWORD,\n EVENT_ID,\n EVENT_KIND,\n TEXT,\n WORD,\n getEventKind,\n} from '../generated/events.generated.js';\nimport { BinaryReader } from '../io/BinaryReader.js';\nimport { BinaryWriter } from '../io/BinaryWriter.js';\n\n/** Event IDs in the DWORD range (128-191) that are explicitly mapped in EVENT_KIND. */\nconst KNOWN_DWORD_IDS: ReadonlySet<number> = new Set(\n Object.keys(EVENT_KIND)\n .map(Number)\n .filter((id) => id >= DWORD && id < TEXT),\n);\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\nconst LOOKAHEAD_BYTES = 200;\nconst LOOKAHEAD_VARINT_MAX = 100_000;\n\n/**\n * Reads a VarInt directly from a raw buffer at the given offset.\n * Returns `{ size, bytesRead }` or `{ size: -1, bytesRead: 0 }` on failure.\n */\nfunction readVarIntRaw(\n buf: Buffer,\n off: number,\n end: number,\n): { size: number; bytesRead: number } {\n let shift = 0;\n let size = 0;\n const start = off;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (off >= end) return { size: -1, bytesRead: 0 };\n const byte = buf[off]!;\n off++;\n size |= (byte & 0x7f) << shift;\n shift += 7;\n if (!(byte & 0x80)) break;\n }\n return { size, bytesRead: off - start };\n}\n\n/**\n * Scores an alignment hypothesis by parsing ahead from `off` for up to\n * `bytesToCheck` bytes. TEXT/DATA events with valid VarInt sizes are\n * the strongest signal of correct alignment; runs of low-value unknown\n * BYTE events (typical UTF-16-LE misread) are penalised.\n */\nfunction scoreAlignment(buf: Buffer, off: number, end: number, bytesToCheck: number): number {\n const limit = Math.min(off + bytesToCheck, end);\n let textDataCount = 0;\n let consecutiveSmallByte = 0;\n let maxConsecutiveSmallByte = 0;\n\n while (off < limit) {\n const id = buf[off]!;\n\n if (id < WORD) {\n off += 2;\n if (id < 32 && !KNOWN_DWORD_IDS.has(id)) {\n consecutiveSmallByte++;\n maxConsecutiveSmallByte = Math.max(maxConsecutiveSmallByte, consecutiveSmallByte);\n } else {\n consecutiveSmallByte = 0;\n }\n } else if (id < DWORD) {\n off += 3;\n consecutiveSmallByte = 0;\n } else if (id < TEXT) {\n off += 5;\n consecutiveSmallByte = 0;\n } else {\n const vi = readVarIntRaw(buf, off + 1, end);\n if (vi.size >= 0 && vi.size < LOOKAHEAD_VARINT_MAX && off + 1 + vi.bytesRead + vi.size <= end) {\n textDataCount++;\n off = off + 1 + vi.bytesRead + vi.size;\n } else {\n return -100;\n }\n consecutiveSmallByte = 0;\n }\n }\n\n return textDataCount * 10 - maxConsecutiveSmallByte * 3;\n}\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 if (eventId < WORD) {\n payloadSize = 1;\n headerSize = 1;\n } else if (eventId < DWORD) {\n payloadSize = 2;\n headerSize = 1;\n } else if (eventId < TEXT) {\n if (KNOWN_DWORD_IDS.has(eventId)) {\n payloadSize = 4;\n headerSize = 1;\n } else {\n // Unknown DWORD-range event: use look-ahead to decide encoding\n const afterDword = eventStart + 1 + 4;\n const vi = readVarIntRaw(buffer, eventStart + 1, eventsEnd);\n\n if (vi.size < 0 || vi.size > LOOKAHEAD_VARINT_MAX || eventStart + 1 + vi.bytesRead + vi.size > eventsEnd) {\n payloadSize = 4;\n headerSize = 1;\n } else if (vi.size === 3) {\n // VarInt(3) produces the same total footprint as DWORD; prefer DWORD\n payloadSize = 4;\n headerSize = 1;\n } else {\n const afterVarInt = eventStart + 1 + vi.bytesRead + vi.size;\n const dwordScore = scoreAlignment(buffer, afterDword, eventsEnd, LOOKAHEAD_BYTES);\n const varIntScore = scoreAlignment(buffer, afterVarInt, eventsEnd, LOOKAHEAD_BYTES);\n\n if (varIntScore > dwordScore + 2) {\n payloadSize = vi.size;\n reader.seek(eventStart + 1 + vi.bytesRead);\n headerSize = 1 + vi.bytesRead;\n } else {\n payloadSize = 4;\n headerSize = 1;\n }\n }\n }\n } else {\n // TEXT / DATA: always VarInt-encoded size\n const sizeStart = reader.tell();\n payloadSize = reader.readVarInt();\n headerSize = 1 + (reader.tell() - sizeStart);\n }\n\n const payload = reader.readBytes(payloadSize);\n const header = buffer.subarray(eventStart, eventStart + headerSize);\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 the first FLVersion event\n if (eventId === EVENT_ID.PROJECT_FL_VERSION && flVersion === '0.0.0') {\n const candidate = payload.toString('ascii').replace(/\\0/g, '').trim();\n if (/^\\d+(\\.\\d+)+$/.test(candidate)) {\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 }\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 *\n * For unchanged events the original header is emitted verbatim, ensuring a\n * byte-identical round-trip. For newly created events (empty header) the\n * header is rebuilt: TEXT/DATA events get a VarInt-encoded size prefix,\n * BYTE/WORD/DWORD events get only the 1-byte ID.\n */\nfunction serializeEvent(event: FlpEvent): Buffer {\n const writer = new BinaryWriter();\n\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 writer.writeBytes(event.payload);\n\n return writer.toBuffer();\n}\n\n/**\n * Creates a new event with the given ID and payload.\n *\n * For TEXT/DATA events (id >= 192) the header includes a VarInt size prefix.\n * For BYTE/WORD/DWORD events the header is a single byte (the event ID).\n */\nexport function createEvent(id: number, payload: Buffer): FlpEvent {\n const kind = getEventKind(id);\n\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 and the original event used a VarInt-encoded header\n // (header > 1 byte), recalculate the header with the new size.\n if (patched.payload !== event.payload && event.header.length > 1) {\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 the BPM value from the parsed FLP.\n *\n * Primary: PROJECT_TEMPO (DWORD+28) stores BPM × 1000 as u32.\n * Legacy fallback: _TEMPO_COARSE (WORD+2) + _TEMPO_FINE (WORD+29).\n */\nfunction readBpm(parsed: ParsedFlp): number | null {\n const tempoEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TEMPO);\n if (tempoEvent) {\n return getEventNumber(tempoEvent) / 1000;\n }\n\n const coarseEvent = findFirstEvent(parsed, EVENT_ID.PROJECT__TEMPO_COARSE);\n if (coarseEvent) {\n let bpm = coarseEvent.payload.readUInt16LE(0);\n const fineEvent = findFirstEvent(parsed, EVENT_ID.PROJECT__TEMPO_FINE);\n if (fineEvent) {\n bpm += fineEvent.payload.readUInt16LE(0) / 1000;\n }\n return bpm;\n }\n\n return 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\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: readBpm(parsed),\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(parsed: ParsedFlp, info: Partial<ProjectTimeInfo>): 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 (\n !seenPlugins.has(key) &&\n currentInternalName &&\n currentInternalName !== 'Fruity Wrapper'\n ) {\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"]}
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// FL Studio 25+ writes this otherwise-unknown DWORD-range event with 3 payload bytes.\r\nconst FIXED_SIZE_OVERRIDES: Readonly<Record<number, number>> = {\r\n 0xac: 3,\r\n};\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 [0xac]: 'unknown',\r\n\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 * 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 const override = FIXED_SIZE_OVERRIDES[eventId];\r\n if (override !== undefined) {\r\n return override;\r\n }\r\n\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 * Event payload sizes follow the deterministic FL Studio event ranges,\n * including explicit overrides for known format exceptions.\n */\n\nimport { EVENT_ID, TEXT, getEventFixedSize, 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 reader.readU16LE(); // Channel count is preserved in headerChunkBytes.\n reader.readU16LE(); // PPQ is exposed from headerChunkBytes by getPPQ().\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 const fixedSize = getEventFixedSize(eventId);\n\n if (fixedSize >= 0) {\n payloadSize = fixedSize;\n headerSize = 1;\n } else {\n // TEXT / DATA: always VarInt-encoded size\n const sizeStart = reader.tell();\n payloadSize = reader.readVarInt();\n headerSize = 1 + (reader.tell() - sizeStart);\n }\n\n const payload = reader.readBytes(payloadSize);\n const header = buffer.subarray(eventStart, eventStart + headerSize);\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 the first FLVersion event\n if (eventId === EVENT_ID.PROJECT_FL_VERSION && flVersion === '0.0.0') {\n const candidate = payload.toString('ascii').replace(/\\0/g, '').trim();\n if (/^\\d+(\\.\\d+)+$/.test(candidate)) {\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 }\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 *\n * For unchanged events the original header is emitted verbatim, ensuring a\n * byte-identical round-trip. For newly created events (empty header) the\n * header is rebuilt: TEXT/DATA events get a VarInt-encoded size prefix,\n * BYTE/WORD/DWORD events get only the 1-byte ID.\n */\nfunction serializeEvent(event: FlpEvent): Buffer {\n const writer = new BinaryWriter();\n\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 writer.writeBytes(event.payload);\n\n return writer.toBuffer();\n}\n\n/**\n * Creates a new event with the given ID and payload.\n *\n * For TEXT/DATA events (id >= 192) the header includes a VarInt size prefix.\n * For BYTE/WORD/DWORD events the header is a single byte (the event ID).\n */\nexport function createEvent(id: number, payload: Buffer): FlpEvent {\n const kind = getEventKind(id);\n\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 and the original event used a VarInt-encoded header\n // (header > 1 byte), recalculate the header with the new size.\n if (patched.payload !== event.payload && event.header.length > 1) {\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 createEvent,\n createNumberPayload,\n createTextPayload,\n findFirstEvent,\n type FlpEvent,\n getEventNumber,\n getEventString,\n type ParsedFlp,\n patchEvents,\n} from '../parser/FlpParser.js';\n\nconst PROJECT_EVENT_ORDER = [\n EVENT_ID.PROJECT_TEMPO,\n EVENT_ID.PROJECT_TITLE,\n EVENT_ID.PROJECT_GENRE,\n EVENT_ID.PROJECT_ARTISTS,\n EVENT_ID.PROJECT_COMMENTS,\n EVENT_ID.PROJECT_TIMESTAMP,\n] as const;\n\n/** Inserts a project-level event without moving any existing event. */\nfunction insertProjectEvent(parsed: ParsedFlp, event: FlpEvent): ParsedFlp {\n const orderIndex = PROJECT_EVENT_ORDER.indexOf(event.id as (typeof PROJECT_EVENT_ORDER)[number]);\n\n let insertionIndex = -1;\n if (orderIndex >= 0) {\n const laterIds = new Set<number>(PROJECT_EVENT_ORDER.slice(orderIndex + 1));\n insertionIndex = parsed.events.findIndex((candidate) => laterIds.has(candidate.id));\n\n if (insertionIndex < 0) {\n const previousIds = new Set<number>(PROJECT_EVENT_ORDER.slice(0, orderIndex + 1));\n for (let index = parsed.events.length - 1; index >= 0; index--) {\n if (previousIds.has(parsed.events[index]!.id)) {\n insertionIndex = index + 1;\n break;\n }\n }\n }\n }\n\n if (insertionIndex < 0) {\n insertionIndex = parsed.events.findIndex((candidate) => candidate.id === EVENT_ID.CHANNEL_NEW);\n }\n\n if (insertionIndex < 0) {\n const versionIndex = parsed.events.findIndex(\n (candidate) => candidate.id === EVENT_ID.PROJECT_FL_VERSION,\n );\n insertionIndex = versionIndex >= 0 ? versionIndex + 1 : parsed.events.length;\n }\n\n const events = [...parsed.events];\n events.splice(insertionIndex, 0, event);\n return { ...parsed, events };\n}\n\nfunction replaceEventPayload(parsed: ParsedFlp, target: FlpEvent, payload: Buffer): ParsedFlp {\n return patchEvents(parsed, (event) => (event === target ? { ...event, payload } : event));\n}\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 the BPM value from the parsed FLP.\n *\n * Primary: PROJECT_TEMPO (DWORD+28) stores BPM × 1000 as u32.\n * Legacy fallback: _TEMPO_COARSE (WORD+2) + _TEMPO_FINE (WORD+29).\n */\nfunction readBpm(parsed: ParsedFlp): number | null {\n const tempoEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TEMPO);\n if (tempoEvent) {\n return getEventNumber(tempoEvent) / 1000;\n }\n\n const coarseEvent = findFirstEvent(parsed, EVENT_ID.PROJECT__TEMPO_COARSE);\n if (coarseEvent) {\n let bpm = coarseEvent.payload.readUInt16LE(0);\n const fineEvent = findFirstEvent(parsed, EVENT_ID.PROJECT__TEMPO_FINE);\n if (fineEvent) {\n bpm += fineEvent.payload.readUInt16LE(0) / 1000;\n }\n return bpm;\n }\n\n return 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\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: readBpm(parsed),\n };\n}\n\nfunction compareVersion(version: string, expected: readonly number[]): number {\n const actual = version.split('.').map((part) => Number.parseInt(part, 10));\n const length = Math.max(actual.length, expected.length);\n\n for (let index = 0; index < length; index++) {\n const difference = (actual[index] ?? 0) - (expected[index] ?? 0);\n if (difference !== 0) return Math.sign(difference);\n }\n\n return 0;\n}\n\nfunction validateBpm(parsed: ParsedFlp, bpm: number): void {\n if (!Number.isFinite(bpm)) {\n throw new RangeError('BPM must be a finite number');\n }\n\n const legacyHighTempo =\n compareVersion(parsed.flVersion, [1, 4, 2]) >= 0 && compareVersion(parsed.flVersion, [11]) < 0;\n const maximum = legacyHighTempo ? 999 : 522;\n\n if (bpm < 10 || bpm > maximum) {\n throw new RangeError(`BPM must be between 10 and ${maximum} for FL Studio ${parsed.flVersion}`);\n }\n\n if (\n parsed.flVersion !== '0.0.0' &&\n compareVersion(parsed.flVersion, [3, 4]) < 0 &&\n !Number.isInteger(bpm)\n ) {\n throw new TypeError(`FL Studio ${parsed.flVersion} does not support decimal BPM values`);\n }\n}\n\nfunction writeTextProjectEvent(\n parsed: ParsedFlp,\n eventId: number,\n value: string | null | undefined,\n): ParsedFlp {\n if (value === undefined) return parsed;\n\n const payload = createTextPayload(value ?? '', parsed.useUnicode);\n const existing = findFirstEvent(parsed, eventId);\n return existing\n ? replaceEventPayload(parsed, existing, payload)\n : insertProjectEvent(parsed, createEvent(eventId, payload));\n}\n\nfunction writeProjectBpm(parsed: ParsedFlp, bpm: number | null | undefined): ParsedFlp {\n if (bpm === undefined || bpm === null) return parsed;\n validateBpm(parsed, bpm);\n\n const scaledBpm = Math.round(bpm * 1000);\n const coarseBpm = Math.floor(scaledBpm / 1000);\n const fineBpm = scaledBpm % 1000;\n const modernEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TEMPO);\n const coarseEvent = findFirstEvent(parsed, EVENT_ID.PROJECT__TEMPO_COARSE);\n const fineEvent = findFirstEvent(parsed, EVENT_ID.PROJECT__TEMPO_FINE);\n\n let result = parsed;\n\n if (modernEvent) {\n result = replaceEventPayload(result, modernEvent, createNumberPayload(scaledBpm, 'u32'));\n }\n\n if (coarseEvent) {\n result = replaceEventPayload(result, coarseEvent, createNumberPayload(coarseBpm, 'u16'));\n }\n\n if (fineEvent) {\n result = replaceEventPayload(result, fineEvent, createNumberPayload(fineBpm, 'u16'));\n }\n\n if (!modernEvent && !coarseEvent && !fineEvent) {\n result = insertProjectEvent(\n result,\n createEvent(EVENT_ID.PROJECT_TEMPO, createNumberPayload(scaledBpm, 'u32')),\n );\n } else if (!modernEvent && coarseEvent && !fineEvent && fineBpm > 0) {\n const updatedCoarse = findFirstEvent(result, EVENT_ID.PROJECT__TEMPO_COARSE)!;\n const index = result.events.indexOf(updatedCoarse) + 1;\n const events = [...result.events];\n events.splice(\n index,\n 0,\n createEvent(EVENT_ID.PROJECT__TEMPO_FINE, createNumberPayload(fineBpm, 'u16')),\n );\n result = { ...result, events };\n } else if (!modernEvent && !coarseEvent && fineEvent) {\n const updatedFine = findFirstEvent(result, EVENT_ID.PROJECT__TEMPO_FINE)!;\n const index = result.events.indexOf(updatedFine);\n const events = [...result.events];\n events.splice(\n index,\n 0,\n createEvent(EVENT_ID.PROJECT__TEMPO_COARSE, createNumberPayload(coarseBpm, 'u16')),\n );\n result = { ...result, events };\n }\n\n return result;\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 let result = parsed;\n result = writeProjectBpm(result, meta.bpm);\n result = writeTextProjectEvent(result, EVENT_ID.PROJECT_TITLE, meta.name);\n result = writeTextProjectEvent(result, EVENT_ID.PROJECT_GENRE, meta.genre);\n result = writeTextProjectEvent(result, EVENT_ID.PROJECT_ARTISTS, meta.artist);\n result = writeTextProjectEvent(result, EVENT_ID.PROJECT_COMMENTS, meta.description);\n return result;\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_MS = Date.UTC(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_MS + 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_MS;\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(parsed: ParsedFlp, info: Partial<ProjectTimeInfo>): ParsedFlp {\n if (info.creationDate === undefined && info.workTimeSeconds === undefined) {\n return parsed;\n }\n\n const current = readProjectTimeInfo(parsed);\n\n if (\n info.creationDate !== undefined &&\n info.creationDate !== null &&\n Number.isNaN(info.creationDate.getTime())\n ) {\n throw new RangeError('creationDate must be a valid Date');\n }\n\n if (\n info.workTimeSeconds !== undefined &&\n info.workTimeSeconds !== null &&\n (!Number.isFinite(info.workTimeSeconds) || info.workTimeSeconds < 0)\n ) {\n throw new RangeError('workTimeSeconds must be a finite, non-negative number');\n }\n\n const creationDateDays =\n info.creationDate === null\n ? 0\n : info.creationDate !== undefined\n ? dateToDelphiDays(info.creationDate)\n : current.creationDate !== null\n ? dateToDelphiDays(current.creationDate)\n : 0;\n const workTimeDays =\n info.workTimeSeconds === null\n ? 0\n : info.workTimeSeconds !== undefined\n ? info.workTimeSeconds / (24 * 60 * 60)\n : current.workTimeSeconds !== null\n ? current.workTimeSeconds / (24 * 60 * 60)\n : 0;\n\n const writer = new BinaryWriter();\n writer.writeF64LE(creationDateDays);\n writer.writeF64LE(workTimeDays);\n\n const timestampEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TIMESTAMP);\n return timestampEvent\n ? replaceEventPayload(parsed, timestampEvent, writer.toBuffer())\n : insertProjectEvent(parsed, createEvent(EVENT_ID.PROJECT_TIMESTAMP, writer.toBuffer()));\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 (\n !seenPlugins.has(key) &&\n currentInternalName &&\n currentInternalName !== 'Fruity Wrapper'\n ) {\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"]}