diff src/edl.c @ 8:0c98b46eaf73 v2.0

*: update API to 2.0, big changes all APIs now use pointers to an EDL object. it is up to the user to make sure that the pointer is valid. additionally, many things have been separated into new files to make it easier to digest
author Paper <paper@paper.us.eu.org>
date Sun, 03 Mar 2024 17:56:58 -0500
parents 7137fbac0b85
children 0cc2555db371
line wrap: on
line diff
--- a/src/edl.c	Mon Jan 15 06:42:30 2024 -0500
+++ b/src/edl.c	Sun Mar 03 17:56:58 2024 -0500
@@ -3,273 +3,229 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
+
 #include "edl.h"
 #include "str.h"
-
-#define strnchr(haystack, needle, length) (char*)memchr(haystack, needle, strnlen(haystack, length))
-
-static uint32_t EDL_internal_crcn32b(const unsigned char* restrict message, size_t length) {
-	uint32_t crc = 0xFFFFFFFF;
+#include "datatypes.h"
+#include "util.h"
 
+/* CRC32 hashes of EDL headers */
+#define EDL_HEADER_ID				0x1541c503
+#define EDL_HEADER_TRACK			0x4b211812
+#define EDL_HEADER_STARTTIME		0xbb46516f
+#define EDL_HEADER_LENGTH			0xaeac5df7
+#define EDL_HEADER_PLAYRATE			0x03834606
+#define EDL_HEADER_LOCKED			0x0c2083d3
+#define EDL_HEADER_NORMALIZED		0xe60c8b1d
+#define EDL_HEADER_STRETCHMETHOD	0xbe5802c9
+#define EDL_HEADER_LOOPED			0x4ec8be4c
+#define EDL_HEADER_ONRULER			0xe6cb84d1
+#define EDL_HEADER_MEDIATYPE		0x035f84c2
+#define EDL_HEADER_FILENAME			0x379d32c5
+#define EDL_HEADER_STREAM			0x9f334738
+#define EDL_HEADER_STREAMSTART		0x07b4e0e7
+#define EDL_HEADER_STREAMLENGTH		0x8f16c7b8
+#define EDL_HEADER_FADETIMEIN		0xd2edd7e4
+#define EDL_HEADER_FADETIMEOUT		0x792e8c40
+#define EDL_HEADER_SUSTAINGAIN		0x98374657
+#define EDL_HEADER_CURVEIN			0x3e998b1f
+#define EDL_HEADER_GAININ			0x82fb09c4
+#define EDL_HEADER_CURVEOUT			0x53add388
+#define EDL_HEADER_GAINOUT			0x4210ba56
+#define EDL_HEADER_LAYER			0x89c4df6c
+#define EDL_HEADER_COLOR			0xadf2f1a3
+#define EDL_HEADER_CURVEINR			0xa56b43e1
+#define EDL_HEADER_CURVEOUTR		0xcb6d715e
+#define EDL_HEADER_PLAYPITCH		0x9da1b9ed
+#define EDL_HEADER_LOCKPITCH		0x2bda6ed4
+#define EDL_HEADER_FIRSTCHANNEL		0x3071a4c6
+#define EDL_HEADER_CHANNELS			0xe94981a4
+
+/* use a CRC32 hash function to process the headers */
+static uint32_t EDL_internal_crcn32b(const unsigned char* message, size_t length) {
+	uint32_t crc = 0xffffffff;
 	size_t i;
+
 	for (i = 0; i < length && message[i]; i++) {
+		size_t j;
+
 		crc = crc ^ message[i];
-		size_t j;
 		for (j = 0; j < 8; j++)
-			crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
+			crc = (crc >> 1) ^ (0xedb88320 & -(crc & 1));
 	}
 
 	return ~crc;
 }
 
-/* This parses the first line of the EDL and
- * creates an array with CRC32 hashes in
- * the order of the strings.
+static size_t EDL_internal_size_t_min(size_t a, size_t b) {
+	return (a < b) ? a : b;
+}
+
+/* This parses the header of the EDL and creates an array with CRC32 hashes
+ * in the order of the strings.
 */
-static uint32_t* EDL_internal_parse_first_line(const char* data, size_t* restrict length) {
-	size_t hashes_size = 32, current_hash = 0;
+static uint32_t* EDL_internal_parse_header(const char* data, size_t* length) {
+	/* */
+	const size_t newline = EDL_internal_size_t_min(strchr(data, '\n') - data, strchr(data, '\r') - data);
+
+	size_t current_hash = 0, hashes_size = 32;
 	uint32_t* hashes = malloc(hashes_size * sizeof(uint32_t));
 
-	size_t len_until_newline = strchr(data, '\n') - data, len_until_cr = strchr(data, '\r') - data;
-	size_t i, b;
-	for (i = 0, b = 0; i <= len_until_newline && i <= len_until_cr; i++, b++) {
-		if (data[i] == ';' || data[i] == ':' || data[i] == '\r' || data[i] == '\n') {
-			if (current_hash >= hashes_size)
+	/* overall iterates over the entire line,
+	 * column stores the length of the current column */
+	size_t overall, column;
+
+	for (overall = 0, column = 0; overall <= newline; overall++) {
+		if (data[overall] == ';' || data[overall] == ':') {
+			/* process the current column */
+			if (hashes_size < current_hash)
 				hashes = realloc(hashes, (hashes_size *= 2) * sizeof(uint32_t));
-			hashes[current_hash++] = EDL_internal_crcn32b((const unsigned char*)&data[i - b], b);
-			b = -1; // ew
-		}
+
+			hashes[current_hash++] = EDL_internal_crcn32b((const unsigned char*)&data[overall - column], column);
+			column = 0; /* reset */
+		} else column++;
 	}
 
-	*length = current_hash; // wtf?
+	*length = current_hash;
 
 	return hashes;
 }
 
-/* -- Functions to extract different datatypes -- */
-
-static size_t EDL_internal_append_offset(const char* input, size_t offset, size_t length) {
-	size_t s = 0;
-
-	s += strchr(&input[offset + s], ';') - &input[offset];
-	if (s + offset > length)
-		return s;
-
-	s += strspn(&input[offset + s], ";\t ");
-	if (s + offset > length)
-		return s;
-
-	return s;
-}
-
-static size_t EDL_internal_get_int(const char* input, size_t offset, size_t length, int* int_return) {
-	if (offset > length)
-		return 0;
-
-	{
-		char* ptr_return;
-		*int_return = strtol(&input[offset], &ptr_return, 10);
-
-		if (!ptr_return)
-			return 0;
-	}
-
-	return EDL_internal_append_offset(input, offset, length);
-}
-
-static size_t EDL_internal_get_double(const char* input, size_t offset, size_t length, double* double_return) {
-	if (offset > length)
-		return 0;
-
-	{
-		char* ptr_return;
-		*double_return = strtod(&input[offset], &ptr_return);
-
-		if (!ptr_return)
-			return 0;
-	}
-
-	return EDL_internal_append_offset(input, offset, length);
-}
-
-static size_t EDL_internal_get_bool(const char* input, size_t offset, size_t length, bool* bool_return) {
-	if (offset > length)
+int EDL_reallocate(EDL* edl, size_t new_capacity) {
+	edl->arr = realloc(edl->arr, new_capacity * sizeof(EDL_line));
+	if (!edl->arr)
 		return 0;
 
-	if (!strncmp(&input[offset], "TRUE", 4))
-		*bool_return = true;
-	else if (!strncmp(&input[offset], "FALSE", 5))
-		*bool_return = false;
-
-	return EDL_internal_append_offset(input, offset, length);
-}
-
-static size_t EDL_internal_get_media_type(const char* input, size_t offset, size_t length, MediaType* media_return) {
-	if (offset > length)
-		return 0;
-
-	if (!strncmp(&input[offset], "VIDEO", 5))
-		*media_return = MEDIATYPE_VIDEO;
-	else if (!strncmp(&input[offset], "AUDIO", 5))
-		*media_return = MEDIATYPE_AUDIO;
-	else
-		*media_return = MEDIATYPE_UNKNOWN;
-
-	return EDL_internal_append_offset(input, offset, length);
-}
-
-static size_t EDL_internal_get_string(const char* input, size_t offset, size_t length, char** string_return) {
-	/* Windows filenames will *NEVER* include double quotes.
-	   This might break with EDL files created with Reaper on Linux. */
-	if (offset > length)
-		return 0;
+	if (new_capacity > edl->capacity)
+		memset(&edl->arr[edl->capacity], 0, (new_capacity - edl->capacity) * sizeof(EDL_line));
 
-	size_t start = strchr(&input[offset], '\"') - &input[offset] + 1;
-	if (start + offset > length)
-		return 0;
-
-	size_t s_length = strchr(&input[offset + start], '\"') - &input[offset] - start;
-	if (start + s_length + offset > length)
-		return 0;
-
-	*string_return = malloc((s_length + 1) * sizeof(char));
-	if (!*string_return)
-		return 0;
-
-	memcpy(*string_return, &input[offset + start], s_length);
-	(*string_return)[s_length] = '\0';
-
-	return EDL_internal_append_offset(input, offset, length);
-}
-
-/* memory management routines */
-
-int EDL_reallocate(EDL* input, size_t new_capacity) {
-	input->arr = realloc(input->arr, new_capacity * sizeof(EDL_line));
-	if (!input->arr)
-		return 0;
-
-	if (new_capacity > input->capacity)
-		memset(&input->arr[input->capacity], 0, (new_capacity - input->capacity) * sizeof(EDL_line));
-
-	input->capacity = new_capacity;
+	edl->capacity = new_capacity;
 
 	return 1;
 }
 
-/* the important function */
-EDL EDL_parse(const char* data, size_t length) {
-	EDL edl = {0};
-	if (!EDL_reallocate(&edl, 16))
-		return edl;
+int EDL_append(EDL* edl, EDL_line line) {
+	if (edl->size + 1 < edl->capacity && !EDL_reallocate(edl, edl->capacity))
+		return 0;
 
+	edl->arr[++edl->size] = line;
+	return 1;
+}
+
+/* the big important function */
+EDL_parse_error_t EDL_parse(EDL* edl, const char* data, size_t length) {
 	size_t order_size = 0;
-	uint32_t* order = EDL_internal_parse_first_line(data, &order_size);
-
+	uint32_t* order = NULL;
 	size_t offset = 0;
-	while ((offset = strnchr(&data[offset], '\n', length - offset) - data + 1) < length) {
-		size_t local_offset = offset; // this is so our original offset stays intact
+
+	if (!EDL_reallocate(edl, 16))
+		return EDL_PARSE_ERROR_OUT_OF_MEMORY;
+
+	order = EDL_internal_parse_header(data, &order_size);
 
+	while ((offset = EDL_internal_strnchr(&data[offset], '\n', length - offset) - data + 1) < length) {
+		size_t local_offset = offset; /* original offset stays intact */
 		bool ok = true;
+		int i;
 
-		for (int i = 0; i < order_size; i++) {
+		for (i = 0; i < order_size; i++) {
 #define ADD_TO_OFFSET(x, a) \
 { \
-	size_t o = a(data, local_offset, length, &edl.arr[edl.size].x); \
+	size_t o = a(data, local_offset, length, &edl->arr[edl->size].x); \
 	if (!o) \
 		ok = false; \
 	local_offset += o; \
 }
 			switch (order[i]) {
-				case 0x1541c503: /* ID */
-					ADD_TO_OFFSET(id, EDL_internal_get_int);
+				case EDL_HEADER_ID:
+					ADD_TO_OFFSET(id, EDL_internal_parse_int);
 					break;
-				case 0x4b211812: /* Track */
-					ADD_TO_OFFSET(track, EDL_internal_get_int);
+				case EDL_HEADER_TRACK:
+					ADD_TO_OFFSET(track, EDL_internal_parse_int);
 					break;
-				case 0xbb46516f: /* StartTime */
-					ADD_TO_OFFSET(start_time, EDL_internal_get_double);
+				case EDL_HEADER_STARTTIME:
+					ADD_TO_OFFSET(start_time, EDL_internal_parse_double);
 					break;
-				case 0xaeac5df7: /* Length */
-					ADD_TO_OFFSET(length, EDL_internal_get_double);
+				case EDL_HEADER_LENGTH:
+					ADD_TO_OFFSET(length, EDL_internal_parse_double);
 					break;
-				case 0x03834606: /* PlayRate */
-					ADD_TO_OFFSET(play_rate, EDL_internal_get_double);
+				case EDL_HEADER_PLAYRATE:
+					ADD_TO_OFFSET(play_rate, EDL_internal_parse_double);
 					break;
-				case 0x0c2083d3: /* Locked */
-					ADD_TO_OFFSET(locked, EDL_internal_get_bool);
+				case EDL_HEADER_LOCKED:
+					ADD_TO_OFFSET(locked, EDL_internal_parse_bool);
 					break;
-				case 0xe60c8b1d: /* Normalized */
-					ADD_TO_OFFSET(normalized, EDL_internal_get_bool);
+				case EDL_HEADER_NORMALIZED:
+					ADD_TO_OFFSET(normalized, EDL_internal_parse_bool);
 					break;
-				case 0xbe5802c9: /* StretchMethod */
-					ADD_TO_OFFSET(stretch_method, EDL_internal_get_int);
+				case EDL_HEADER_STRETCHMETHOD:
+					ADD_TO_OFFSET(stretch_method, EDL_internal_parse_int);
 					break;
-				case 0x4ec8be4c: /* Looped */
-					ADD_TO_OFFSET(looped, EDL_internal_get_bool);
+				case EDL_HEADER_LOOPED:
+					ADD_TO_OFFSET(looped, EDL_internal_parse_bool);
 					break;
-				case 0xe6cb84d1: /* OnRuler */
-					ADD_TO_OFFSET(on_ruler, EDL_internal_get_bool);
+				case EDL_HEADER_ONRULER:
+					ADD_TO_OFFSET(on_ruler, EDL_internal_parse_bool);
 					break;
-				case 0x035f84c2: /* MediaType */
-					ADD_TO_OFFSET(media_type, EDL_internal_get_media_type);
+				case EDL_HEADER_MEDIATYPE:
+					ADD_TO_OFFSET(media_type, EDL_internal_parse_media_type);
 					break;
-				case 0x379d32c5: /* FileName */
-					ADD_TO_OFFSET(filename, EDL_internal_get_string);
+				case EDL_HEADER_FILENAME:
+					ADD_TO_OFFSET(filename, EDL_internal_parse_string);
 					break;
-				case 0x9f334738: /* Stream */
-					ADD_TO_OFFSET(stream, EDL_internal_get_int);
+				case EDL_HEADER_STREAM:
+					ADD_TO_OFFSET(stream, EDL_internal_parse_int);
 					break;
-				case 0x07b4e0e7: /* StreamStart */
-					ADD_TO_OFFSET(stream_start, EDL_internal_get_double);
+				case EDL_HEADER_STREAMSTART:
+					ADD_TO_OFFSET(stream_start, EDL_internal_parse_double);
 					break;
-				case 0x8f16c7b8: /* StreamLength */
-					ADD_TO_OFFSET(stream_length, EDL_internal_get_double);
+				case EDL_HEADER_STREAMLENGTH:
+					ADD_TO_OFFSET(stream_length, EDL_internal_parse_double);
 					break;
-				case 0xd2edd7e4: /* FadeTimeIn */
-					ADD_TO_OFFSET(fade_time_in, EDL_internal_get_double);
+				case EDL_HEADER_FADETIMEIN:
+					ADD_TO_OFFSET(fade_time_in, EDL_internal_parse_double);
 					break;
-				case 0x792e8c40: /* FadeTimeOut */
-					ADD_TO_OFFSET(fade_time_out, EDL_internal_get_double);
+				case EDL_HEADER_FADETIMEOUT:
+					ADD_TO_OFFSET(fade_time_out, EDL_internal_parse_double);
 					break;
-				case 0x98374657: /* SustainGain */
-					ADD_TO_OFFSET(sustain_gain, EDL_internal_get_double);
+				case EDL_HEADER_SUSTAINGAIN:
+					ADD_TO_OFFSET(sustain_gain, EDL_internal_parse_double);
 					break;
-				case 0x3e998b1f: /* CurveIn */
-					ADD_TO_OFFSET(curve_in, EDL_internal_get_int);
+				case EDL_HEADER_CURVEIN:
+					ADD_TO_OFFSET(curve_in, EDL_internal_parse_int);
 					break;
-				case 0x82fb09c4: /* GainIn */
-					ADD_TO_OFFSET(gain_in, EDL_internal_get_double);
+				case EDL_HEADER_GAININ:
+					ADD_TO_OFFSET(gain_in, EDL_internal_parse_double);
 					break;
-				case 0x53add388: /* CurveOut */
-					ADD_TO_OFFSET(curve_out, EDL_internal_get_int);
+				case EDL_HEADER_CURVEOUT:
+					ADD_TO_OFFSET(curve_out, EDL_internal_parse_int);
 					break;
-				case 0x4210ba56: /* GainOut */
-					ADD_TO_OFFSET(gain_out, EDL_internal_get_double);
+				case EDL_HEADER_GAINOUT:
+					ADD_TO_OFFSET(gain_out, EDL_internal_parse_double);
 					break;
-				case 0x89c4df6c: /* Layer */
-					ADD_TO_OFFSET(layer, EDL_internal_get_int);
+				case EDL_HEADER_LAYER:
+					ADD_TO_OFFSET(layer, EDL_internal_parse_int);
 					break;
-				case 0xadf2f1a3: /* Color */
-					ADD_TO_OFFSET(color, EDL_internal_get_int);
+				case EDL_HEADER_COLOR:
+					ADD_TO_OFFSET(color, EDL_internal_parse_int);
 					break;
-				case 0xa56b43e1: /* CurveInR */
-					ADD_TO_OFFSET(curve_in_r, EDL_internal_get_int);
+				case EDL_HEADER_CURVEINR:
+					ADD_TO_OFFSET(curve_in_r, EDL_internal_parse_int);
 					break;
-				case 0xcb6d715e: /* CurveOutR */
-					ADD_TO_OFFSET(curve_out_r, EDL_internal_get_int);
+				case EDL_HEADER_CURVEOUTR:
+					ADD_TO_OFFSET(curve_out_r, EDL_internal_parse_int);
 					break;
-				case 0x9da1b9ed: /* PlayPitch */
-					ADD_TO_OFFSET(play_pitch, EDL_internal_get_double);
+				case EDL_HEADER_PLAYPITCH:
+					ADD_TO_OFFSET(play_pitch, EDL_internal_parse_double);
 					break;
-				case 0x2bda6ed4: /* LockPitch */
-					ADD_TO_OFFSET(lock_pitch, EDL_internal_get_bool);
+				case EDL_HEADER_LOCKPITCH:
+					ADD_TO_OFFSET(lock_pitch, EDL_internal_parse_bool);
 					break;
-				case 0x3071a4c6: /* FirstChannel */
-					ADD_TO_OFFSET(first_channel, EDL_internal_get_int);
+				case EDL_HEADER_FIRSTCHANNEL:
+					ADD_TO_OFFSET(first_channel, EDL_internal_parse_int);
 					break;
-				case 0xe94981a4: /* Channels */
-					ADD_TO_OFFSET(channels, EDL_internal_get_int);
+				case EDL_HEADER_CHANNELS:
+					ADD_TO_OFFSET(channels, EDL_internal_parse_int);
 					break;
 				default:
 					/* ... what */
@@ -281,145 +237,119 @@
 		if (!ok)
 			break;
 
-		if (++edl.size >= edl.capacity)
-			EDL_reallocate(&edl, edl.capacity * 2);
+		if (++edl->size >= edl->capacity)
+			EDL_reallocate(edl, edl->capacity * 2);
 	}
 
-	EDL_reallocate(&edl, edl.size);
+	/* put on the shrinkwrap */
+	EDL_reallocate(edl, edl->size);
 
 	free(order);
 
-	return edl;
-}
-
-static char* EDL_internal_integer_to_string(int value) {
-	char out[256] = {0}; // this ought to be enough.
-	snprintf(out, 256, "%d", value);
-	out[255] = '\0';
-	return strdup(out);
-}
-
-static char* EDL_internal_double_to_string(double value) {
-	char out[256] = {0};
-	snprintf(out, 256, "%.6f", value);
-	out[255] = '\0';
-	return strdup(out);
+	return EDL_PARSE_ERROR_SUCCESS;
 }
 
-static char* EDL_internal_bool_to_string(bool value) {
-	return strdup(value ? "TRUE" : "FALSE");
-}
+static void EDL_dump_line(EDL_internal_string* str, const EDL_line* line, const uint32_t* order, const size_t order_len) {
+	size_t i;
 
-static char* EDL_internal_media_type_to_string(MediaType value) {
-	switch (value) {
-		case MEDIATYPE_AUDIO:
-			return strdup("AUDIO");
-		case MEDIATYPE_VIDEO:
-		case MEDIATYPE_UNKNOWN:
-		default:
-			return strdup("VIDEO");
-	}
-}
-
-static void EDL_dump_line(EDL_internal_string* str, EDL_line line, const uint32_t* order, const size_t order_len) {
-	for (size_t i = 0; i < order_len; i++) {
+	for (i = 0; i < order_len; i++) {
 		switch (order[i]) {
 #define APPEND_ITEM(a, x) \
 { \
-	char* tstr = x(line.a); \
+	char* tstr = x(line->a); \
 	EDL_internal_string_append(str, tstr, strlen(tstr)); \
 	free(tstr); \
 }
-			case 0x1541c503: /* ID */
+			case EDL_HEADER_ID:
 				APPEND_ITEM(id, EDL_internal_integer_to_string);
 				break;
-			case 0x4b211812: /* Track */
+			case EDL_HEADER_TRACK:
 				APPEND_ITEM(track, EDL_internal_integer_to_string);
 				break;
-			case 0xbb46516f: /* StartTime */
+			case EDL_HEADER_STARTTIME:
 				APPEND_ITEM(start_time, EDL_internal_double_to_string);
 				break;
-			case 0xaeac5df7: /* Length */
+			case EDL_HEADER_LENGTH:
 				APPEND_ITEM(length, EDL_internal_double_to_string);
 				break;
-			case 0x03834606: /* PlayRate */
+			case EDL_HEADER_PLAYRATE:
 				APPEND_ITEM(play_rate, EDL_internal_double_to_string);
 				break;
-			case 0x0c2083d3: /* Locked */
+			case EDL_HEADER_LOCKED:
 				APPEND_ITEM(locked, EDL_internal_bool_to_string);
 				break;
-			case 0xe60c8b1d: /* Normalized */
+			case EDL_HEADER_NORMALIZED:
 				APPEND_ITEM(normalized, EDL_internal_bool_to_string);
 				break;
-			case 0xbe5802c9: /* StretchMethod */
+			case EDL_HEADER_STRETCHMETHOD:
 				APPEND_ITEM(stretch_method, EDL_internal_integer_to_string);
 				break;
-			case 0x4ec8be4c: /* Looped */
+			case EDL_HEADER_LOOPED:
 				APPEND_ITEM(looped, EDL_internal_bool_to_string);
 				break;
-			case 0xe6cb84d1: /* OnRuler */
+			case EDL_HEADER_ONRULER:
 				APPEND_ITEM(on_ruler, EDL_internal_bool_to_string);
 				break;
-			case 0x035f84c2: /* MediaType */
+			case EDL_HEADER_MEDIATYPE:
 				APPEND_ITEM(media_type, EDL_internal_media_type_to_string);
 				break;
-			case 0x379d32c5: /* FileName */
+			case EDL_HEADER_FILENAME:
 				EDL_internal_string_append(str, "\"", 1);
-				EDL_internal_string_append(str, line.filename, strlen(line.filename));
+				EDL_internal_string_append(str, line->filename, strlen(line->filename));
 				EDL_internal_string_append(str, "\"", 1);
 				break;
-			case 0x9f334738: /* Stream */
+			case EDL_HEADER_STREAM:
 				APPEND_ITEM(stream, EDL_internal_integer_to_string);
 				break;
-			case 0x07b4e0e7: /* StreamStart */
+			case EDL_HEADER_STREAMSTART:
 				APPEND_ITEM(stream_start, EDL_internal_double_to_string);
 				break;
-			case 0x8f16c7b8: /* StreamLength */
+			case EDL_HEADER_STREAMLENGTH:
 				APPEND_ITEM(stream_length, EDL_internal_double_to_string);
 				break;
-			case 0xd2edd7e4: /* FadeTimeIn */
+			case EDL_HEADER_FADETIMEIN:
 				APPEND_ITEM(fade_time_in, EDL_internal_double_to_string);
 				break;
-			case 0x792e8c40: /* FadeTimeOut */
+			case EDL_HEADER_FADETIMEOUT:
 				APPEND_ITEM(fade_time_out, EDL_internal_double_to_string);
 				break;
-			case 0x98374657: /* SustainGain */
+			case EDL_HEADER_SUSTAINGAIN:
 				APPEND_ITEM(sustain_gain, EDL_internal_double_to_string);
 				break;
-			case 0x3e998b1f: /* CurveIn */
+			case EDL_HEADER_CURVEIN:
 				APPEND_ITEM(curve_in, EDL_internal_integer_to_string);
 				break;
-			case 0x82fb09c4: /* GainIn */
+			case EDL_HEADER_GAININ:
 				APPEND_ITEM(gain_in, EDL_internal_double_to_string);
 				break;
-			case 0x53add388: /* CurveOut */
+			case EDL_HEADER_CURVEOUT:
 				APPEND_ITEM(curve_out, EDL_internal_integer_to_string);
 				break;
-			case 0x4210ba56: /* GainOut */
+			case EDL_HEADER_GAINOUT:
 				APPEND_ITEM(gain_out, EDL_internal_double_to_string);
 				break;
-			case 0x89c4df6c: /* Layer */
+			case EDL_HEADER_LAYER:
 				APPEND_ITEM(layer, EDL_internal_integer_to_string);
 				break;
-			case 0xadf2f1a3: /* Color */
+			case EDL_HEADER_COLOR:
 				APPEND_ITEM(color, EDL_internal_integer_to_string);
 				break;
-			case 0xa56b43e1: /* CurveInR */
+			case EDL_HEADER_CURVEINR:
 				APPEND_ITEM(curve_in_r, EDL_internal_integer_to_string);
 				break;
-			case 0xcb6d715e: /* CurveOutR */
+			case EDL_HEADER_CURVEOUTR:
 				APPEND_ITEM(curve_out_r, EDL_internal_integer_to_string);
 				break;
-			case 0x9da1b9ed: /* PlayPitch */
+			case EDL_HEADER_PLAYPITCH:
 				APPEND_ITEM(play_pitch, EDL_internal_double_to_string);
 				break;
-			case 0x2bda6ed4: /* LockPitch */
+			case EDL_HEADER_LOCKPITCH:
 				APPEND_ITEM(lock_pitch, EDL_internal_bool_to_string);
 				break;
-			case 0x3071a4c6: /* FirstChannel */
+			case EDL_HEADER_FIRSTCHANNEL:
 				APPEND_ITEM(first_channel, EDL_internal_integer_to_string);
 				break;
-			case 0xe94981a4: /* Channels */
+			case EDL_HEADER_CHANNELS:
 				APPEND_ITEM(channels, EDL_internal_integer_to_string);
 				break;
 			default:
@@ -437,32 +367,38 @@
 	EDL_internal_string_append(str, "\n", 1);
 }
 
-char* EDL_dump(EDL edl) {
+/* this gets progressively slower and slower... oops */
+char* EDL_dump(const EDL* edl) {
+	static const char order_str[] =
+		"\"ID\";\"Track\";\"StartTime\";\"Length\";\"PlayRate\";\"Locked\";\"Normalized\";\"StretchMethod\";"
+		"\"Looped\";\"OnRuler\";\"MediaType\";\"FileName\";\"Stream\";\"StreamStart\";\"StreamLength\";"
+		"\"FadeTimeIn\";\"FadeTimeOut\";\"SustainGain\";\"CurveIn\";\"GainIn\";\"CurveOut\";\"GainOut\";"
+		"\"Layer\";\"Color\";\"CurveInR\";\"CurveOutR\":\"PlayPitch\";\"LockPitch\";\"FirstChannel\";\"Channels\"\n";
+
 	EDL_internal_string ret;
 	EDL_internal_string_init(&ret);
 
-	static const char order_str[] = "\"ID\";\"Track\";\"StartTime\";\"Length\";\"PlayRate\";\"Locked\";\"Normalized\";\"StretchMethod\";\"Looped\";\"OnRuler\";\"MediaType\";\"FileName\";\"Stream\";\"StreamStart\";\"StreamLength\";\"FadeTimeIn\";\"FadeTimeOut\";\"SustainGain\";\"CurveIn\";\"GainIn\";\"CurveOut\";\"GainOut\";\"Layer\";\"Color\";\"CurveInR\";\"CurveOutR\":\"PlayPitch\";\"LockPitch\";\"FirstChannel\";\"Channels\"\n";
 	EDL_internal_string_append(&ret, order_str, strlen(order_str));
 
-	size_t order_len;
-	uint32_t* order = EDL_internal_parse_first_line(order_str, &order_len);
+	{
+		size_t order_len;
+		uint32_t* order = EDL_internal_parse_header(order_str, &order_len);
 
-	size_t i;
-	for (i = 0; i < edl.size; i++)
-		EDL_dump_line(&ret, edl.arr[i], order, order_len);
+		size_t i;
+		for (i = 0; i < edl->size; i++)
+			EDL_dump_line(&ret, &edl->arr[i], order, order_len);
 
-	free(order);
-
-	EDL_internal_string_allocate(&ret, ret.size);
+		free(order);
+	}
 
 	return ret.data;
 }
 
-void EDL_free(EDL edl) {
+void EDL_free(EDL* edl) {
 	size_t i;
-	for (i = 0; i < edl.size; i++) {
-		if (edl.arr[i].filename)
-			free(edl.arr[i].filename);
+	for (i = 0; i < edl->size; i++) {
+		if (edl->arr[i].filename)
+			free(edl->arr[i].filename);
 	}
-	free(edl.arr);
+	free(edl->arr);
 }