# HG changeset patch # User Paper # Date 1703466090 18000 # Node ID d00bc412900eb121c5b026283d52d819c3f3d6f9 # Parent db8b7e45ee55ffb3bcf17b201bb23ccb0c7331b4 *: fix up lots of stuff and make the thing actually usable diff -r db8b7e45ee55 -r d00bc412900e .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sun Dec 24 20:01:30 2023 -0500 @@ -0,0 +1,11 @@ +syntax: regexp +^autom4te.cache +^m4 +^build-aux +^build + +syntax: glob +Makefile.in +configure +configure~ +aclocal.m4 diff -r db8b7e45ee55 -r d00bc412900e LICENSE --- a/LICENSE Tue Jul 04 22:50:26 2023 -0400 +++ b/LICENSE Sun Dec 24 20:01:30 2023 -0500 @@ -1,21 +1,29 @@ -MIT License +BSD 3-Clause License -Copyright (c) 2023 Paper +Copyright (c) 2023, Paper. +All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -r db8b7e45ee55 -r d00bc412900e Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.am Sun Dec 24 20:01:30 2023 -0500 @@ -0,0 +1,6 @@ +lib_LTLIBRARIES = libedl.la +libedl_la_SOURCES = src/main.c +include_HEADERS = include/edl.h + +AM_CPPFLAGS = -I$(srcdir)/include +ACLOCAL_AMFLAGS = -I m4 diff -r db8b7e45ee55 -r d00bc412900e README.md --- a/README.md Tue Jul 04 22:50:26 2023 -0400 +++ b/README.md Sun Dec 24 20:01:30 2023 -0500 @@ -1,5 +1,43 @@ -# edl-parser -edl-parser is a parser library for Vegas Pro EDL files and translates them into a usable format for manipulation in C/C++. +# libedl +libedl is a library for parsing Vegas Pro EDL files and translating them into usable C structures. + +## Usage +```c +#include +#include "edl.h" + +int main(){ + /* open the file */ + FILE* file = fopen("MyProject.txt", "rb"); + if (!file) + return 1; + + /* get filesize */ + fseek(file, 0L, SEEK_END); + long fsize = ftell(file); + fseek(file, 0L, SEEK_SET); -# How do I use this? -Drag it into your C/C++ project and adjust your compiler flags accordingly. Then you can call `parse_EDL` with a `char*` containing the entire contents of any EDL file. \ No newline at end of file + /* grab the contents */ + char* data = malloc(fsize + 1); + if (!data) + return 1; + + fread(data, fsize, 1, file); + + fclose(file); + + /* pass it to libedl */ + EDL_file edl = EDL_parse(data, fsize + 1); + + /* do what we have to do with it */ + for (int i = 0; i < edl.size; i++) { + printf("%s", edl.arr[i].filename); + } + + /* free our memory */ + EDL_free(edl); + free(data); + + return 0; +} +``` diff -r db8b7e45ee55 -r d00bc412900e configure.ac --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configure.ac Sun Dec 24 20:01:30 2023 -0500 @@ -0,0 +1,18 @@ +AC_INIT([libedl], [1.0]) + +AC_CONFIG_SRCDIR([src/main.c]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIRS([m4]) + +AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) + +# Check for a C compiler +AC_PROG_CC + +# Need this because libtool is weird +AM_PROG_AR + +LT_INIT + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff -r db8b7e45ee55 -r d00bc412900e include/edl.h --- a/include/edl.h Tue Jul 04 22:50:26 2023 -0400 +++ b/include/edl.h Sun Dec 24 20:01:30 2023 -0500 @@ -1,8 +1,16 @@ -#include +#ifndef __edl__edl_h +#define __edl__edl_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* C99 required */ +#include typedef enum { - video, - audio + MEDIATYPE_VIDEO, + MEDIATYPE_AUDIO } MediaType; typedef struct { @@ -17,7 +25,7 @@ bool looped; bool on_ruler; MediaType media_type; - char* file_name; + char* filename; int stream; double stream_start; double stream_length; @@ -39,7 +47,16 @@ } EDL_line; typedef struct { - EDL_line* edl; + EDL_line* arr; + int capacity; int size; - int current; -} EDL_file; \ No newline at end of file +} EDL_file; + +EDL_file EDL_parse(const char* text, size_t length); +void EDL_free(EDL_file file); + +#ifdef __cplusplus +} +#endif + +#endif diff -r db8b7e45ee55 -r d00bc412900e src/main.c --- a/src/main.c Tue Jul 04 22:50:26 2023 -0400 +++ b/src/main.c Sun Dec 24 20:01:30 2023 -0500 @@ -5,221 +5,301 @@ #include #include "edl.h" -int strings_amt = -1; +#define strnchr(haystack, needle, length) memchr(haystack, needle, strnlen(haystack, length)) -/* CRC32 hash function */ -uint32_t crc32b(unsigned char* restrict message) { - int32_t i, j; - uint32_t byte, crc, mask; +static uint32_t EDL_internal_crcn32b(const unsigned char* restrict message, size_t length) { + uint32_t crc = 0xFFFFFFFF; - crc = 0xFFFFFFFF; - for (i = 0; message[i] != 0; i++) { - byte = message[i]; // Get next byte. - crc = crc ^ byte; - for (j = 7; j >= 0; j--) { // Do eight times. - mask = -(crc & 1); - crc = (crc >> 1) ^ (0xEDB88320 & mask); - } + size_t i; + for (i = 0; i < length && message[i]; i++) { + crc = crc ^ message[i]; + size_t j; + for (j = 0; j < 8; j++) + 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. */ -uint32_t* parse_first_line(char* first_line) { - uint32_t* hashes = calloc(32, sizeof(uint32_t)); - int i = 0; - char* token; - hashes[i++] = crc32b(strtok_r(first_line, ";", &token)); - for (;;i++) { - char* token = strtok_r(NULL, ";", &token); - if (token == NULL) - break; - hashes[i] = crc32b(token); + * 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; + 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] == ';') { + 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); + b = -1; // ew + } } - strings_amt = i; - free(first_line); + + *length = current_hash; + return hashes; } /* -- Functions to extract different datatypes -- */ -char* get_int(char* input, int* int_return) { - char* ptr_return; - *int_return = strtol(input, &ptr_return, 10); - return ptr_return + 2; +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; } -char* get_double(char* input, double* double_return) { - char* ptr_return; - *double_return = strtod(input, &ptr_return); - return ptr_return + 2; +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); } -char* get_bool(char* input, bool* bool_return) { - switch (input[0]) { - case 'T': - *bool_return = true; - return input + 6; - case 'F': - *bool_return = false; - return input + 7; - default: - /* what */ - return NULL; +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); } -char* get_media_type(char* input, MediaType* media_return) { - switch (input[0]) { - case 'V': - *media_return = video; - return input + 7; - case 'A': - *media_return = audio; - return input + 7; - default: - /* what */ - return NULL; - } +static size_t EDL_internal_get_bool(const char* input, size_t offset, size_t length, bool* bool_return) { + if (offset > length) + 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; + + return EDL_internal_append_offset(input, offset, length); } -/* lenptr is optional */ -char* cut_string_at_match(char* input, char* match, int* lenptr) { - int length = strcspn(&input[1], match); - char* string = calloc(length+1, sizeof(char)); - memcpy(string, &input[1], length); - string[length] = '\0'; - if (lenptr != NULL) - *lenptr = length; - return string; -} - -char* get_string(char* input, char** string_return) { +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. */ - int length; - char* string = cut_string_at_match(input, "\"", &length); - *string_return = string; - return input + length + 4; + if (offset > length) + return 0; + + 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); } -/* The memory manager for EDL_file. */ -void allocate_memory(EDL_file* input) { - if (input->size == 0) input->size = 16; - input->edl = realloc(input->edl, (input->size*2) * sizeof(EDL_line)); - input->size *= 2; +/* memory management routines */ + +static int EDL_internal_reallocate(EDL_file* 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); + + 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); } -/* This is the important function */ -EDL_file* parse_EDL(char* text) { - EDL_file edl; - memset(&edl, '\0', sizeof(edl)); - allocate_memory(&edl); - uint32_t* order = parse_first_line(strtok_r(text, "\n", NULL)); - char* line = strtok(text, "\n"); - while ((line = strtok(NULL, "\n")) != NULL) { - char* tmp_ptr = line; - for (int i = 0; i < strings_amt; i++) { +/* the important function */ +EDL_file EDL_parse(const char* data, size_t length) { + EDL_file edl = {0}; + if (!EDL_internal_allocate_memory(&edl)) + return edl; + + size_t order_size = 0; + uint32_t* order = EDL_internal_parse_first_line(data, &order_size); + + size_t offset = 0; + while ((offset = strnchr(data + offset + 1, '\n', length - offset) - (void*)data) < length) { + size_t local_offset = offset; // this is so our original offset stays intact + + bool ok = true; + + for (int 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); \ + if (!o) \ + ok = false; \ + local_offset += o; \ +} switch (order[i]) { case 0x1541c503: /* ID */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].id); + ADD_TO_OFFSET(id, EDL_internal_get_int); break; case 0x4b211812: /* Track */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].track); + ADD_TO_OFFSET(track, EDL_internal_get_int); break; case 0xbb46516f: /* StartTime */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].start_time); + ADD_TO_OFFSET(start_time, EDL_internal_get_double); break; case 0xaeac5df7: /* Length */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].length); + ADD_TO_OFFSET(length, EDL_internal_get_double); break; case 0x03834606: /* PlayRate */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].play_rate); + ADD_TO_OFFSET(play_rate, EDL_internal_get_double); break; case 0x0c2083d3: /* Locked */ - tmp_ptr = get_bool(tmp_ptr, &edl.edl[edl.current].locked); + ADD_TO_OFFSET(locked, EDL_internal_get_bool); break; case 0xe60c8b1d: /* Normalized */ - tmp_ptr = get_bool(tmp_ptr, &edl.edl[edl.current].normalized); + ADD_TO_OFFSET(normalized, EDL_internal_get_bool); break; case 0xbe5802c9: /* StretchMethod */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].stretch_method); + ADD_TO_OFFSET(stretch_method, EDL_internal_get_int); break; case 0x4ec8be4c: /* Looped */ - tmp_ptr = get_bool(tmp_ptr, &edl.edl[edl.current].looped); + ADD_TO_OFFSET(looped, EDL_internal_get_bool); break; case 0xe6cb84d1: /* OnRuler */ - tmp_ptr = get_bool(tmp_ptr, &edl.edl[edl.current].on_ruler); + ADD_TO_OFFSET(on_ruler, EDL_internal_get_bool); break; case 0x035f84c2: /* MediaType */ - tmp_ptr = get_media_type(tmp_ptr, &edl.edl[edl.current].media_type); + ADD_TO_OFFSET(media_type, EDL_internal_get_media_type); break; case 0x379d32c5: /* FileName */ - tmp_ptr = get_string(tmp_ptr, &edl.edl[edl.current].file_name); + ADD_TO_OFFSET(filename, EDL_internal_get_string); break; case 0x9f334738: /* Stream */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].stream); + ADD_TO_OFFSET(stream, EDL_internal_get_int); break; case 0x07b4e0e7: /* StreamStart */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].stream_start); + ADD_TO_OFFSET(stream_start, EDL_internal_get_double); break; case 0x8f16c7b8: /* StreamLength */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].stream_length); + ADD_TO_OFFSET(stream_length, EDL_internal_get_double); break; case 0xd2edd7e4: /* FadeTimeIn */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].fade_time_in); + ADD_TO_OFFSET(fade_time_in, EDL_internal_get_double); break; case 0x792e8c40: /* FadeTimeOut */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].fade_time_out); + ADD_TO_OFFSET(fade_time_out, EDL_internal_get_double); break; case 0x98374657: /* SustainGain */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].sustain_gain); + ADD_TO_OFFSET(sustain_gain, EDL_internal_get_double); break; case 0x3e998b1f: /* CurveIn */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].curve_in); + ADD_TO_OFFSET(curve_in, EDL_internal_get_int); break; case 0x82fb09c4: /* GainIn */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].gain_in); + ADD_TO_OFFSET(gain_in, EDL_internal_get_double); break; case 0x53add388: /* CurveOut */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].curve_out); + ADD_TO_OFFSET(curve_out, EDL_internal_get_int); break; case 0x4210ba56: /* GainOut */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].gain_out); + ADD_TO_OFFSET(gain_out, EDL_internal_get_double); break; case 0x89c4df6c: /* Layer */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].layer); + ADD_TO_OFFSET(layer, EDL_internal_get_int); break; case 0xadf2f1a3: /* Color */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].color); + ADD_TO_OFFSET(color, EDL_internal_get_int); break; case 0xa56b43e1: /* CurveInR */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].curve_in_r); + ADD_TO_OFFSET(curve_in_r, EDL_internal_get_int); break; case 0xcb6d715e: /* CurveOutR */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].curve_out_r); + ADD_TO_OFFSET(curve_out_r, EDL_internal_get_int); break; case 0x9da1b9ed: /* PlayPitch */ - tmp_ptr = get_double(tmp_ptr, &edl.edl[edl.current].play_pitch); + ADD_TO_OFFSET(play_pitch, EDL_internal_get_double); break; case 0x2bda6ed4: /* LockPitch */ - tmp_ptr = get_bool(tmp_ptr, &edl.edl[edl.current].lock_pitch); + ADD_TO_OFFSET(lock_pitch, EDL_internal_get_bool); break; case 0x3071a4c6: /* FirstChannel */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].first_channel); + ADD_TO_OFFSET(first_channel, EDL_internal_get_int); break; case 0x7de1bd40: /* Channels */ - tmp_ptr = get_int(tmp_ptr, &edl.edl[edl.current].channels); + ADD_TO_OFFSET(channels, EDL_internal_get_int); break; default: /* ... what */ break; } } - edl.current++; - if (edl.current >= edl.size) - allocate_memory(&edl); + + if (!ok) + break; + + if (++edl.size >= edl.capacity) + EDL_internal_allocate_memory(&edl); } + + EDL_internal_shrink_memory_to_fit(&edl); + + free(order); + + return edl; } + +void EDL_free(EDL_file file) { + size_t i; + for (i = 0; i < file.size; i++) { + if (file.arr[i].filename) + free(file.arr[i].filename); + } + free(file.arr); +}