Mercurial > libedl
view src/edl.c @ 6:7137fbac0b85
edl: expose EDL_reallocate() API to allow resizing the array
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Thu, 28 Dec 2023 16:47:24 -0500 |
parents | c2408abb258a |
children | 0c98b46eaf73 |
line wrap: on
line source
#include <string.h> #include <stdbool.h> #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; 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, 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); b = -1; // ew } } *length = current_hash; // wtf? 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; 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; 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; return 1; } /* the important function */ EDL EDL_parse(const char* data, size_t length) { EDL edl = {0}; if (!EDL_reallocate(&edl, 16)) 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], '\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 0xe94981a4: /* Channels */ ADD_TO_OFFSET(channels, EDL_internal_get_int); break; default: /* ... what */ break; #undef ADD_TO_OFFSET } } if (!ok) break; if (++edl.size >= edl.capacity) EDL_reallocate(&edl, edl.capacity * 2); } 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); } 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 < 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(edl.arr); }