Mercurial > libedl
diff src/edl.c @ 3:bd99b6549eb4
*: expand in readme and rename src/main to src/edl
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Sun, 24 Dec 2023 20:22:54 -0500 |
parents | |
children | c2408abb258a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/edl.c Sun Dec 24 20:22:54 2023 -0500 @@ -0,0 +1,305 @@ +#include <string.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include "edl.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; + + 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. +*/ +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 + } + } + + *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) + 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); +} + +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; + + 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 */ + +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); +} + +/* 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 = strnchr(data, '\n', length) - data + 1; + 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; + + 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 */ + ADD_TO_OFFSET(id, EDL_internal_get_int); + break; + case 0x4b211812: /* Track */ + ADD_TO_OFFSET(track, EDL_internal_get_int); + break; + case 0xbb46516f: /* StartTime */ + ADD_TO_OFFSET(start_time, EDL_internal_get_double); + break; + case 0xaeac5df7: /* Length */ + ADD_TO_OFFSET(length, EDL_internal_get_double); + break; + case 0x03834606: /* PlayRate */ + ADD_TO_OFFSET(play_rate, EDL_internal_get_double); + break; + case 0x0c2083d3: /* Locked */ + ADD_TO_OFFSET(locked, EDL_internal_get_bool); + break; + case 0xe60c8b1d: /* Normalized */ + ADD_TO_OFFSET(normalized, EDL_internal_get_bool); + break; + case 0xbe5802c9: /* StretchMethod */ + ADD_TO_OFFSET(stretch_method, EDL_internal_get_int); + break; + case 0x4ec8be4c: /* Looped */ + ADD_TO_OFFSET(looped, EDL_internal_get_bool); + break; + case 0xe6cb84d1: /* OnRuler */ + ADD_TO_OFFSET(on_ruler, EDL_internal_get_bool); + break; + case 0x035f84c2: /* MediaType */ + ADD_TO_OFFSET(media_type, EDL_internal_get_media_type); + break; + case 0x379d32c5: /* FileName */ + ADD_TO_OFFSET(filename, EDL_internal_get_string); + break; + case 0x9f334738: /* Stream */ + ADD_TO_OFFSET(stream, EDL_internal_get_int); + break; + case 0x07b4e0e7: /* StreamStart */ + ADD_TO_OFFSET(stream_start, EDL_internal_get_double); + break; + case 0x8f16c7b8: /* StreamLength */ + ADD_TO_OFFSET(stream_length, EDL_internal_get_double); + break; + case 0xd2edd7e4: /* FadeTimeIn */ + ADD_TO_OFFSET(fade_time_in, EDL_internal_get_double); + break; + case 0x792e8c40: /* FadeTimeOut */ + ADD_TO_OFFSET(fade_time_out, EDL_internal_get_double); + break; + case 0x98374657: /* SustainGain */ + ADD_TO_OFFSET(sustain_gain, EDL_internal_get_double); + break; + case 0x3e998b1f: /* CurveIn */ + ADD_TO_OFFSET(curve_in, EDL_internal_get_int); + break; + case 0x82fb09c4: /* GainIn */ + ADD_TO_OFFSET(gain_in, EDL_internal_get_double); + break; + case 0x53add388: /* CurveOut */ + ADD_TO_OFFSET(curve_out, EDL_internal_get_int); + break; + case 0x4210ba56: /* GainOut */ + ADD_TO_OFFSET(gain_out, EDL_internal_get_double); + break; + case 0x89c4df6c: /* Layer */ + ADD_TO_OFFSET(layer, EDL_internal_get_int); + break; + case 0xadf2f1a3: /* Color */ + ADD_TO_OFFSET(color, EDL_internal_get_int); + break; + case 0xa56b43e1: /* CurveInR */ + ADD_TO_OFFSET(curve_in_r, EDL_internal_get_int); + break; + case 0xcb6d715e: /* CurveOutR */ + ADD_TO_OFFSET(curve_out_r, EDL_internal_get_int); + break; + case 0x9da1b9ed: /* PlayPitch */ + ADD_TO_OFFSET(play_pitch, EDL_internal_get_double); + break; + case 0x2bda6ed4: /* LockPitch */ + ADD_TO_OFFSET(lock_pitch, EDL_internal_get_bool); + break; + case 0x3071a4c6: /* FirstChannel */ + ADD_TO_OFFSET(first_channel, EDL_internal_get_int); + break; + case 0x7de1bd40: /* Channels */ + ADD_TO_OFFSET(channels, EDL_internal_get_int); + break; + default: + /* ... what */ + break; + } + } + + 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); +}