commit 0cf51dea876ae983511df6a00227689e6900409e Author: Balazs Toldi Date: Wed May 24 16:47:32 2023 +0200 Inital commit Signed-off-by: Balazs Toldi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..effe1a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/ +parser +*.caff +*.jpg +*.jpeg +.vscode \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..611e153 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CC = gcc +CFLAGS = -Wall -O2 -Werror -g +LDFLAGS = -ljpeg +TARGET = parser +SRC = main.c caff.c ciff.c + +all: $(TARGET) + +$(TARGET): $(SRC) + $(CC) $(CFLAGS) $(SRC) -o $(TARGET) $(LDFLAGS) + +clean: + rm -f $(TARGET) diff --git a/caff.c b/caff.c new file mode 100644 index 0000000..afc02ce --- /dev/null +++ b/caff.c @@ -0,0 +1,225 @@ +#include "caff.h" +#include + +void print_credits(CAFF_Credits* credits){ + printf("Creator:%s\n",credits->creator); + printf("Date:%d.%d.%d %d:%d\n",credits->year,credits->month,credits->day,credits->hour,credits->minute); +} + + +void caff_init_animation_list(CAFF_Animation_List *a) { + a->array = NULL; + a->length = 0; + a->size = 0; +} + +void caff_add_animation_list(CAFF_Animation_List *a, CAFF_Animation element) { + if (a->length == a->size) { + a->size =(a->size == 0) ? 1 : a->size*2; + CAFF_Animation* new_array = realloc(a->array, a->size * sizeof(CAFF_Animation)); + if(!new_array){ + printf("Failed to allocate memory!\n"); + exit(EXIT_FAILURE); + } + a->array = new_array; + } + a->array[a->length++] = element; +} + +void caff_free_animtion_list(CAFF_Animation_List *a) { + free(a->array); + a->array = NULL; + a->length = a->size = 0; +} + +CAFF* caff_parse_file(const char* filename) { + FILE* file = fopen(filename, "rb"); + if (!file) { + printf("Failed to open CAFF file: %s\n", filename); + return NULL; + } + + CAFF* caff = malloc(sizeof(CAFF)); + caff->credits = NULL; + caff->header = NULL; + caff_init_animation_list(&caff->animations); + // Check for malloc failure here. + size_t anim_count = 0; + while (!feof(file)) { + uint8_t id; + uint64_t length; + fread(&id, 1, 1, file); + fread(&length, 8, 1, file); + + switch (id) { + case 0x1: + if(!caff->header) + caff->header = read_header(file); + break; + case 0x2: + if(!caff->credits) + caff->credits = read_credits(file); + print_credits(caff->credits ); + break; + case 0x3: + anim_count++; + if(caff->header && caff->header->num_anim == anim_count){ + goto end; + } + printf("Here!\n"); + // You might want to append to a list of animations here. + CAFF_Animation* anim = read_animation(file); + + caff_add_animation_list(&caff->animations,*anim); + free(anim); + printf("There!\n"); + goto end; + break; + default: + printf("Unknown block ID: %u\n", id); + goto end; + // Handle unknown block ID here. + } + } +end: + + fclose(file); + printf("Parse END!\n"); + return caff; +} + +CAFF_Header* read_header(FILE* file) { + CAFF_Header* header = malloc(sizeof(CAFF_Header)); + if (!header) { + printf("Failed to allocate memory for CAFF header\n"); + return NULL; + } + + // Read the magic string. + if (fread(header->magic, 1, 4, file) != 4) { + printf("Failed to read magic string from CAFF header\n"); + free(header); + return NULL; + } + + // Check the magic string. + if (strncmp(header->magic, "CAFF", 4) != 0) { + printf("Invalid magic string in CAFF header\n"); + free(header); + return NULL; + } + + // Read the header size. + if (fread(&header->header_size, 8, 1, file) != 1) { + printf("Failed to read header size from CAFF header\n"); + free(header); + return NULL; + } + + // Read the number of animations. + if (fread(&header->num_anim, 8, 1, file) != 1) { + printf("Failed to read number of animations from CAFF header\n"); + free(header); + return NULL; + } + + return header; +} + + +CAFF_Credits* read_credits(FILE* file) { + CAFF_Credits* credits = malloc(sizeof(CAFF_Credits)); + if (!credits) { + printf("Failed to allocate memory for CAFF credits\n"); + return NULL; + } + + // Read the creation date and time. + if (fread(&credits->year, 2, 1, file) != 1 || + fread(&credits->month, 1, 1, file) != 1 || + fread(&credits->day, 1, 1, file) != 1 || + fread(&credits->hour, 1, 1, file) != 1 || + fread(&credits->minute, 1, 1, file) != 1) { + printf("Failed to read creation date and time from CAFF credits\n"); + free(credits); + return NULL; + } + + // Read the length of the creator field. + if (fread(&credits->creator_len, 8, 1, file) != 1) { + printf("Failed to read length of creator field from CAFF credits\n"); + free(credits); + return NULL; + } + + // Allocate memory for the creator field. + credits->creator = malloc(credits->creator_len + 1); + if (!credits->creator) { + printf("Failed to allocate memory for creator field in CAFF credits\n"); + free(credits); + return NULL; + } + + // Read the creator field. + if (fread(credits->creator, 1, credits->creator_len, file) != credits->creator_len) { + printf("Failed to read creator field from CAFF credits\n"); + free(credits->creator); + free(credits); + return NULL; + } + + // Null-terminate the creator string. + credits->creator[credits->creator_len] = '\0'; + + return credits; +} + + +CAFF_Animation* read_animation(FILE* file) { + CAFF_Animation* animation = malloc(sizeof(CAFF_Animation)); + if (!animation) { + printf("Failed to allocate memory for CAFF animation\n"); + return NULL; + } + + // Read the duration. + if (fread(&animation->duration, 8, 1, file) != 1) { + printf("Failed to read duration from CAFF animation\n"); + free(animation); + return NULL; + } + + // Read the CIFF image. + // Note: The read_ciff function needs to be defined to read a CIFF image from a file. + animation->ciff = read_ciff(file); + if (!animation->ciff) { + printf("Failed to read CIFF image from CAFF animation\n"); + free(animation); + return NULL; + } + + return animation; +} + + +void caff_free(CAFF* caff) { + // Free the header + free(caff->header); + caff->header = NULL; + + // Free the credits + if (caff->credits->creator) { + free(caff->credits->creator); + } + free(caff->credits); + caff->credits = NULL; + + // Free the animations + for (size_t i = 0; i < caff->animations.length; i++) { + free_ciff(caff->animations.array[i].ciff); + } + caff_free_animtion_list(&caff->animations); + + // Finally, free the CAFF struct itself + free(caff); +} \ No newline at end of file diff --git a/caff.h b/caff.h new file mode 100644 index 0000000..0e1bc08 --- /dev/null +++ b/caff.h @@ -0,0 +1,46 @@ +#ifndef _CAFF_H_ +#define _CAFF_H_ +#include +#include + +#include "ciff.h" + +typedef struct { + char magic[4]; // The magic string 'CAFF' + uint64_t header_size; // The size of the header + uint64_t num_anim; // The number of animated CIFFs +} CAFF_Header; + +typedef struct { + uint16_t year; // Year of creation + uint8_t month; // Month of creation + uint8_t day; // Day of creation + uint8_t hour; // Hour of creation + uint8_t minute; // Minute of creation + uint64_t creator_len; // Length of the creator field + char* creator; // The creator of the CAFF file +} CAFF_Credits; + +typedef struct { + uint64_t duration; // Duration of the animation in milliseconds + CIFF* ciff; // The CIFF image +} CAFF_Animation; + +typedef struct { + CAFF_Animation *array; + size_t length; + size_t size; +} CAFF_Animation_List; + +typedef struct { + CAFF_Header* header; // The CAFF header + CAFF_Credits* credits; // The CAFF credits + CAFF_Animation_List animations; // The CAFF animations +} CAFF; +CAFF* caff_parse_file(const char* filename); +CAFF_Header* read_header(FILE* file); +CAFF_Credits* read_credits(FILE* file); +CAFF_Animation* read_animation(FILE* file); +void caff_free(CAFF* caff); + +#endif \ No newline at end of file diff --git a/ciff.c b/ciff.c new file mode 100644 index 0000000..98125a2 --- /dev/null +++ b/ciff.c @@ -0,0 +1,131 @@ +#include "ciff.h" +#include +#include +#include +#include + + +CIFF* read_ciff(FILE* file) { + + if (!file) { + printf("Failed to open file!\n"); + return NULL; + } + + CIFF* ciff = malloc(sizeof(CIFF)); + if (!ciff) { + printf("Failed to allocate memory for CIFF structure\n"); + fclose(file); + return NULL; + } + + // Read the header + fread(ciff->magic, 4, 1, file); + fread(&ciff->header_size, 8, 1, file); + fread(&ciff->content_size, 8, 1, file); + fread(&ciff->width, 8, 1, file); + fread(&ciff->height, 8, 1, file); + + // Ensure the file is actually a CIFF file + if (strncmp(ciff->magic, "CIFF", 4) != 0) { + printf("File is not a CIFF file\n"); + free(ciff); + fclose(file); + return NULL; + } + + // Read the caption + ciff->caption = malloc(ciff->header_size); // overestimate the size + if (!ciff->caption) { + printf("Failed to allocate memory for caption\n"); + free(ciff); + fclose(file); + return NULL; + } + + char c; + size_t i = 0; + do { + fread(&c, 1, 1, file); + ciff->caption[i++] = c; + } while (c != '\n'); + ciff->caption[i] = '\0'; // null-terminate the string + size_t caption_lenth= i; + // Read the tags + ciff->tags = malloc(ciff->header_size-sizeof(char)*4-sizeof(uint64_t)*4-caption_lenth); + if (!ciff->tags) { + printf("Failed to allocate memory for tags\n"); + free(ciff->caption); + free(ciff); + fclose(file); + return NULL; + } + + i = 0; + size_t header_loaded = sizeof(char)*4+sizeof(uint64_t)*4+caption_lenth; + printf("Header loaded: %ld/%ld\n",header_loaded,ciff->header_size); + while(header_loaded < ciff->header_size) { + fread(&c, 1, 1, file); + ciff->tags[i++] = c; + header_loaded += sizeof(char); + }; + // Read the pixels + ciff->pixels = malloc(ciff->content_size); + if (!ciff->pixels) { + printf("Failed to allocate memory for pixel data\n"); + free(ciff->caption); + free(ciff); + fclose(file); + return NULL; + } + fread(ciff->pixels, ciff->content_size, 1, file); + + + return ciff; +} + +void generate_jpg(CIFF* ciff, const char* out_filename) { + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + FILE* outfile = fopen(out_filename, "wb"); + if (!outfile) { + printf("Failed to open output file: %s\n", out_filename); + return; + } + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + jpeg_stdio_dest(&cinfo, outfile); + + cinfo.image_width = ciff->width; + cinfo.image_height = ciff->height; + cinfo.input_components = 3; // number of color components per pixel + cinfo.in_color_space = JCS_RGB; // color space of input image + + jpeg_set_defaults(&cinfo); + + jpeg_start_compress(&cinfo, TRUE); + + JSAMPROW row_pointer[1]; + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = &ciff->pixels[cinfo.next_scanline * ciff->width * 3]; + jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + jpeg_finish_compress(&cinfo); + + fclose(outfile); + + jpeg_destroy_compress(&cinfo); +} + + + +void free_ciff(CIFF* ciff) { + free(ciff->caption); + free(ciff->pixels); + free(ciff->tags); + free(ciff); +} diff --git a/ciff.h b/ciff.h new file mode 100644 index 0000000..17770a3 --- /dev/null +++ b/ciff.h @@ -0,0 +1,24 @@ +#ifndef _CIFF_H_ +#define _CIFF_H_ +#include +#include +#include +#include +#define MAX_PATH_LEN 1024 + +typedef struct { + char magic[4]; + uint64_t header_size; + uint64_t content_size; + uint64_t width; + uint64_t height; + char* caption; + char* tags; + uint8_t* pixels; +} CIFF; + +CIFF* read_ciff(FILE* file); +void generate_jpg(CIFF* ciff, const char* out_filename); +void free_ciff(CIFF* ciff); + +#endif \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..4d78d54 --- /dev/null +++ b/main.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +#include "ciff.h" +#include "caff.h" + +int main(int argc, char **argv) +{ + if (argc != 3 || (strcmp(argv[1], "-ciff") != 0 && strcmp(argv[1], "-caff") != 0)) + { + printf("Usage: %s <-ciff|-caff> \n", argv[0]); + return -1; + } + bool is_caff = strcmp(argv[1], "-caff") == 0; + char *input_path = argv[2]; + char jpg_path[MAX_PATH_LEN + 1]; + strncpy(jpg_path, input_path, MAX_PATH_LEN-1); + char *extension = strrchr(jpg_path, '.'); + if (extension) + { + strncpy(extension, ".jpeg", MAX_PATH_LEN - (extension - jpg_path)); + } + printf("%s\n", jpg_path); + CIFF *ciff; + CAFF *caff = NULL; + if (is_caff) + { + caff = caff_parse_file(input_path); + ciff = caff->animations.array[0].ciff; + } + else + { + FILE *ciff_file = fopen(input_path, "rb"); + ciff = read_ciff(ciff_file); + } + if (!ciff) + { + printf("Failed to read CIFF file: %s\n", input_path); + return -1; + } + + generate_jpg(ciff, jpg_path); + if (is_caff) + { + caff_free(caff); + } + else + { + free_ciff(ciff); + } + return 0; +} \ No newline at end of file