changeset 4:c2408abb258a

*: add dumping to string, rename EDL_file to EDL
author Paper <mrpapersonic@gmail.com>
date Mon, 25 Dec 2023 16:24:16 -0500
parents bd99b6549eb4
children a6ab6d9c0dac
files Makefile.am README.md include/edl.h include/str.h src/edl.c src/str.c
diffstat 6 files changed, 279 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.am	Sun Dec 24 20:22:54 2023 -0500
+++ b/Makefile.am	Mon Dec 25 16:24:16 2023 -0500
@@ -1,6 +1,6 @@
 lib_LTLIBRARIES = libedl.la
-libedl_la_SOURCES = src/edl.c
-include_HEADERS = include/edl.h
+libedl_la_SOURCES = src/edl.c src/str.c
+include_HEADERS = include/edl.h include/str.h
 
 AM_CPPFLAGS = -I$(srcdir)/include
 ACLOCAL_AMFLAGS = -I m4
--- a/README.md	Sun Dec 24 20:22:54 2023 -0500
+++ b/README.md	Mon Dec 25 16:24:16 2023 -0500
@@ -1,8 +1,6 @@
 # libedl
 libedl is a library for parsing Vegas Pro EDL files and translating them into usable C structures.
 
-Currently, writing EDL files is not supported by this library.
-
 ## Build
 ```console
 $ autoreconf -i
@@ -19,9 +17,9 @@
 #include <stdlib.h>
 #include "edl.h"
 
-int main(){
+int main() {
     /* open the file */
-    FILE* file = fopen("MyProject.txt", "rb");
+    FILE* file = fopen("intensive care unit.TXT", "rb");
     if (!file)
         return 1;
 
@@ -37,15 +35,17 @@
 
     fread(data, fsize, 1, file);
 
+    data[fsize] = '\0';
+
     fclose(file);
 
     /* pass it to libedl */
-    EDL_file edl = EDL_parse(data, fsize + 1);
+    EDL edl = EDL_parse(data, fsize + 1);
 
-    /* do what we have to do with it */
-    for (int i = 0; i < edl.size; i++) {
-        printf("%s\n", edl.arr[i].filename);
-    }
+    /* dump the EDL to */
+    char* edl_str = EDL_dump(edl);
+    printf("%s\n", edl_str);
+    free(edl_str);
 
     /* free our memory */
     EDL_free(edl);
--- a/include/edl.h	Sun Dec 24 20:22:54 2023 -0500
+++ b/include/edl.h	Mon Dec 25 16:24:16 2023 -0500
@@ -10,7 +10,8 @@
 
 typedef enum {
 	MEDIATYPE_VIDEO,
-	MEDIATYPE_AUDIO
+	MEDIATYPE_AUDIO,
+	MEDIATYPE_UNKNOWN
 } MediaType;
 
 typedef struct {
@@ -50,10 +51,11 @@
 	EDL_line* arr;
 	int capacity;
 	int size;
-} EDL_file;
+} EDL;
 
-EDL_file EDL_parse(const char* text, size_t length);
-void EDL_free(EDL_file file);
+EDL EDL_parse(const char* text, size_t length);
+char* EDL_dump(EDL edl);
+void EDL_free(EDL edl);
 
 #ifdef __cplusplus
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/str.h	Mon Dec 25 16:24:16 2023 -0500
@@ -0,0 +1,17 @@
+#ifndef __edl__internal__str_h
+#define __edl__internal__str_h
+
+#include <stddef.h>
+
+typedef struct {
+    size_t size;
+    size_t capacity;
+    char* data;
+} EDL_internal_string;
+
+int EDL_internal_string_init(EDL_internal_string* str);
+int EDL_internal_string_allocate(EDL_internal_string* str, size_t new_capacity);
+int EDL_internal_string_append(EDL_internal_string* str, const char* data, const size_t length);
+void EDL_internal_string_free(EDL_internal_string* str);
+
+#endif // __edl__internal__str_h
--- a/src/edl.c	Sun Dec 24 20:22:54 2023 -0500
+++ b/src/edl.c	Mon Dec 25 16:24:16 2023 -0500
@@ -4,6 +4,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include "edl.h"
+#include "str.h"
 
 #define strnchr(haystack, needle, length) (char*)memchr(haystack, needle, strnlen(haystack, length))
 
@@ -29,9 +30,10 @@
 	size_t hashes_size = 32, current_hash = 0;
 	uint32_t* hashes = malloc(hashes_size * sizeof(uint32_t));
 
-	size_t len_until_newline = strchr(data, '\n') - data, i, b;
-	for (i = 0, b = 0; i < len_until_newline; i++, b++) {
-		if (data[i] == ';') {
+	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)
 				hashes = realloc(hashes, (hashes_size *= 2) * sizeof(uint32_t));
 			hashes[current_hash++] = EDL_internal_crcn32b((const unsigned char*)&data[i - b], b);
@@ -39,7 +41,7 @@
 		}
 	}
 
-	*length = current_hash;
+	*length = current_hash; // wtf?
 
 	return hashes;
 }
@@ -94,9 +96,9 @@
 	if (offset > length)
 		return 0;
 
-	if (strncmp((input + offset), "TRUE", 4))
+	if (!strncmp(&input[offset], "TRUE", 4))
 		*bool_return = true;
-	else if (strncmp((input + offset), "FALSE", 5))
+	else if (!strncmp(&input[offset], "FALSE", 5))
 		*bool_return = false;
 
 	return EDL_internal_append_offset(input, offset, length);
@@ -106,10 +108,12 @@
 	if (offset > length)
 		return 0;
 
-	if (strncmp(input + offset, "VIDEO", 5))
+	if (!strncmp(&input[offset], "VIDEO", 5))
 		*media_return = MEDIATYPE_VIDEO;
-	else if (strncmp(input + offset, "AUDIO", 5))
+	else if (!strncmp(&input[offset], "AUDIO", 5))
 		*media_return = MEDIATYPE_AUDIO;
+	else
+		*media_return = MEDIATYPE_UNKNOWN;
 
 	return EDL_internal_append_offset(input, offset, length);
 }
@@ -140,38 +144,30 @@
 
 /* memory management routines */
 
-static int EDL_internal_reallocate(EDL_file* input, size_t new_capacity) {
+static int EDL_internal_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);
+		memset(&input->arr[input->capacity], 0, (new_capacity - input->capacity) * sizeof(EDL_line));
 
 	input->capacity = new_capacity;
 
 	return 1;
 }
 
-static int EDL_internal_allocate_memory(EDL_file* input) {
-	return EDL_internal_reallocate(input, input->capacity ? input->capacity * 2 : 16);
-}
-
-static int EDL_internal_shrink_memory_to_fit(EDL_file* input) {
-	return EDL_internal_reallocate(input, input->size);
-}
-
 /* the important function */
-EDL_file EDL_parse(const char* data, size_t length) {
-	EDL_file edl = {0};
-	if (!EDL_internal_allocate_memory(&edl))
+EDL EDL_parse(const char* data, size_t length) {
+	EDL edl = {0};
+	if (!EDL_internal_reallocate(&edl, 16))
 		return edl;
 
 	size_t order_size = 0;
 	uint32_t* order = EDL_internal_parse_first_line(data, &order_size);
 
-	size_t offset = strnchr(data, '\n', length) - data + 1;
-	while ((offset = strnchr(data + offset, '\n', length - offset) - data + 1) < length) {
+	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
 
 		bool ok = true;
@@ -272,12 +268,13 @@
 				case 0x3071a4c6: /* FirstChannel */
 					ADD_TO_OFFSET(first_channel, EDL_internal_get_int);
 					break;
-				case 0x7de1bd40: /* Channels */
+				case 0xe94981a4: /* Channels */
 					ADD_TO_OFFSET(channels, EDL_internal_get_int);
 					break;
 				default:
 					/* ... what */
 					break;
+#undef ADD_TO_OFFSET
 			}
 		}
 
@@ -285,21 +282,187 @@
 			break;
 
 		if (++edl.size >= edl.capacity)
-			EDL_internal_allocate_memory(&edl);
+			EDL_internal_reallocate(&edl, edl.capacity * 2);
 	}
 
-	EDL_internal_shrink_memory_to_fit(&edl);
+	EDL_internal_reallocate(&edl, edl.size);
 
 	free(order);
 
 	return edl;
 }
 
-void EDL_free(EDL_file file) {
+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);
+}
+
+static char* EDL_internal_bool_to_string(bool value) {
+	return strdup(value ? "TRUE" : "FALSE");
+}
+
+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++) {
+		switch (order[i]) {
+#define APPEND_ITEM(a, x) \
+{ \
+	char* tstr = x(line.a); \
+	EDL_internal_string_append(str, tstr, strlen(tstr)); \
+	free(tstr); \
+}
+			case 0x1541c503: /* ID */
+				APPEND_ITEM(id, EDL_internal_integer_to_string);
+				break;
+			case 0x4b211812: /* Track */
+				APPEND_ITEM(track, EDL_internal_integer_to_string);
+				break;
+			case 0xbb46516f: /* StartTime */
+				APPEND_ITEM(start_time, EDL_internal_double_to_string);
+				break;
+			case 0xaeac5df7: /* Length */
+				APPEND_ITEM(length, EDL_internal_double_to_string);
+				break;
+			case 0x03834606: /* PlayRate */
+				APPEND_ITEM(play_rate, EDL_internal_double_to_string);
+				break;
+			case 0x0c2083d3: /* Locked */
+				APPEND_ITEM(locked, EDL_internal_bool_to_string);
+				break;
+			case 0xe60c8b1d: /* Normalized */
+				APPEND_ITEM(normalized, EDL_internal_bool_to_string);
+				break;
+			case 0xbe5802c9: /* StretchMethod */
+				APPEND_ITEM(stretch_method, EDL_internal_integer_to_string);
+				break;
+			case 0x4ec8be4c: /* Looped */
+				APPEND_ITEM(looped, EDL_internal_bool_to_string);
+				break;
+			case 0xe6cb84d1: /* OnRuler */
+				APPEND_ITEM(on_ruler, EDL_internal_bool_to_string);
+				break;
+			case 0x035f84c2: /* MediaType */
+				APPEND_ITEM(media_type, EDL_internal_media_type_to_string);
+				break;
+			case 0x379d32c5: /* FileName */
+				EDL_internal_string_append(str, "\"", 1);
+				EDL_internal_string_append(str, line.filename, strlen(line.filename));
+				EDL_internal_string_append(str, "\"", 1);
+				break;
+			case 0x9f334738: /* Stream */
+				APPEND_ITEM(stream, EDL_internal_integer_to_string);
+				break;
+			case 0x07b4e0e7: /* StreamStart */
+				APPEND_ITEM(stream_start, EDL_internal_double_to_string);
+				break;
+			case 0x8f16c7b8: /* StreamLength */
+				APPEND_ITEM(stream_length, EDL_internal_double_to_string);
+				break;
+			case 0xd2edd7e4: /* FadeTimeIn */
+				APPEND_ITEM(fade_time_in, EDL_internal_double_to_string);
+				break;
+			case 0x792e8c40: /* FadeTimeOut */
+				APPEND_ITEM(fade_time_out, EDL_internal_double_to_string);
+				break;
+			case 0x98374657: /* SustainGain */
+				APPEND_ITEM(sustain_gain, EDL_internal_double_to_string);
+				break;
+			case 0x3e998b1f: /* CurveIn */
+				APPEND_ITEM(curve_in, EDL_internal_integer_to_string);
+				break;
+			case 0x82fb09c4: /* GainIn */
+				APPEND_ITEM(gain_in, EDL_internal_double_to_string);
+				break;
+			case 0x53add388: /* CurveOut */
+				APPEND_ITEM(curve_out, EDL_internal_integer_to_string);
+				break;
+			case 0x4210ba56: /* GainOut */
+				APPEND_ITEM(gain_out, EDL_internal_double_to_string);
+				break;
+			case 0x89c4df6c: /* Layer */
+				APPEND_ITEM(layer, EDL_internal_integer_to_string);
+				break;
+			case 0xadf2f1a3: /* Color */
+				APPEND_ITEM(color, EDL_internal_integer_to_string);
+				break;
+			case 0xa56b43e1: /* CurveInR */
+				APPEND_ITEM(curve_in_r, EDL_internal_integer_to_string);
+				break;
+			case 0xcb6d715e: /* CurveOutR */
+				APPEND_ITEM(curve_out_r, EDL_internal_integer_to_string);
+				break;
+			case 0x9da1b9ed: /* PlayPitch */
+				APPEND_ITEM(play_pitch, EDL_internal_double_to_string);
+				break;
+			case 0x2bda6ed4: /* LockPitch */
+				APPEND_ITEM(lock_pitch, EDL_internal_bool_to_string);
+				break;
+			case 0x3071a4c6: /* FirstChannel */
+				APPEND_ITEM(first_channel, EDL_internal_integer_to_string);
+				break;
+			case 0xe94981a4: /* Channels */
+				APPEND_ITEM(channels, EDL_internal_integer_to_string);
+				break;
+			default:
+				/* ... what */
+				break;
+#undef APPEND_ITEM
+		}
+
+		if (i < order_len - 1)
+			EDL_internal_string_append(str, ";\t", 2);
+		else
+			EDL_internal_string_append(str, ";", 1);
+	}
+
+	EDL_internal_string_append(str, "\n", 1);
+}
+
+char* EDL_dump(EDL edl) {
+	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 i;
-	for (i = 0; i < file.size; i++) {
-		if (file.arr[i].filename)
-			free(file.arr[i].filename);
+	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);
+
+	return ret.data;
+}
+
+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);
 	}
-	free(file.arr);
+	free(edl.arr);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/str.c	Mon Dec 25 16:24:16 2023 -0500
@@ -0,0 +1,51 @@
+#include <stdlib.h>
+#include <string.h>
+#include "str.h"
+
+int EDL_internal_string_init(EDL_internal_string* str) {
+    if (!str)
+        return 0;
+
+    str->size = 0;
+    str->capacity = 0;
+    str->data = NULL;
+    return 1;
+}
+
+int EDL_internal_string_allocate(EDL_internal_string* str, size_t new_capacity) {
+    if (new_capacity == str->capacity)
+        return 1; // nothing to do
+
+    str->data = realloc(str->data, new_capacity * sizeof(char));
+	if (!str->data)
+		return 0;
+
+	if (new_capacity > str->capacity)
+		memset(&str->data[str->capacity], 0, (new_capacity - str->capacity) * sizeof(char));
+
+	str->capacity = new_capacity;
+
+	return 1;
+}
+
+int EDL_internal_string_append(EDL_internal_string* str, const char* data, const size_t length) {
+    /* this sucks, but I'm too lazy to write anything smarter. */
+    {
+        size_t capacity = 1;
+        while (capacity < (str->size + length + 1))
+            capacity *= 2;
+
+        if (capacity < str->capacity || !EDL_internal_string_allocate(str, capacity))
+            return 0;
+    }
+
+    strncat(str->data, data, length);
+    str->size += length;
+
+    return 1;
+}
+
+void EDL_internal_string_free(EDL_internal_string* str) {
+    if (str->data)
+        free(str->data);
+}