* Updated the caca importer to reflect that; only one frame is read at the
moment.
* Added an "utf8cr" export format for UTF-8 + CRLF exports.
* Updated cacaserver to reflect file format changes.
tags/v0.99.beta14
| @@ -31,9 +31,25 @@ | |||||
| #include "cucul.h" | #include "cucul.h" | ||||
| #include "cucul_internals.h" | #include "cucul_internals.h" | ||||
| static inline int sprintu32(char *s, uint32_t x) | |||||
| { | |||||
| s[0] = (uint8_t)(x >> 24); | |||||
| s[1] = (uint8_t)(x >> 16) & 0xff; | |||||
| s[2] = (uint8_t)(x >> 8) & 0xff; | |||||
| s[3] = (uint8_t)(x ) & 0xff; | |||||
| return 4; | |||||
| } | |||||
| static inline int sprintu16(char *s, uint16_t x) | |||||
| { | |||||
| s[0] = (uint8_t)(x >> 8) & 0xff; | |||||
| s[1] = (uint8_t)(x ) & 0xff; | |||||
| return 2; | |||||
| } | |||||
| static int export_caca(cucul_canvas_t *, cucul_buffer_t *); | static int export_caca(cucul_canvas_t *, cucul_buffer_t *); | ||||
| static int export_ansi(cucul_canvas_t *, cucul_buffer_t *); | static int export_ansi(cucul_canvas_t *, cucul_buffer_t *); | ||||
| static int export_utf8(cucul_canvas_t *, cucul_buffer_t *); | |||||
| static int export_utf8(cucul_canvas_t *, cucul_buffer_t *, int); | |||||
| static int export_html(cucul_canvas_t *, cucul_buffer_t *); | static int export_html(cucul_canvas_t *, cucul_buffer_t *); | ||||
| static int export_html3(cucul_canvas_t *, cucul_buffer_t *); | static int export_html3(cucul_canvas_t *, cucul_buffer_t *); | ||||
| static int export_irc(cucul_canvas_t *, cucul_buffer_t *); | static int export_irc(cucul_canvas_t *, cucul_buffer_t *); | ||||
| @@ -90,7 +106,9 @@ cucul_buffer_t * cucul_export_canvas(cucul_canvas_t *cv, char const *format) | |||||
| else if(!strcasecmp("ansi", format)) | else if(!strcasecmp("ansi", format)) | ||||
| ret = export_ansi(cv, ex); | ret = export_ansi(cv, ex); | ||||
| else if(!strcasecmp("utf8", format)) | else if(!strcasecmp("utf8", format)) | ||||
| ret = export_utf8(cv, ex); | |||||
| ret = export_utf8(cv, ex, 0); | |||||
| else if(!strcasecmp("utf8cr", format)) | |||||
| ret = export_utf8(cv, ex, 1); | |||||
| else if(!strcasecmp("html", format)) | else if(!strcasecmp("html", format)) | ||||
| ret = export_html(cv, ex); | ret = export_html(cv, ex); | ||||
| else if(!strcasecmp("html3", format)) | else if(!strcasecmp("html3", format)) | ||||
| @@ -134,6 +152,7 @@ char const * const * cucul_get_export_list(void) | |||||
| "caca", "native libcaca format", | "caca", "native libcaca format", | ||||
| "ansi", "ANSI", | "ansi", "ANSI", | ||||
| "utf8", "UTF-8 with ANSI escape codes", | "utf8", "UTF-8 with ANSI escape codes", | ||||
| "utf8cr", "UTF-8 with ANSI escape codes and MS-DOS \\r", | |||||
| "html", "HTML", | "html", "HTML", | ||||
| "html3", "backwards-compatible HTML", | "html3", "backwards-compatible HTML", | ||||
| "irc", "IRC with mIRC colours", | "irc", "IRC with mIRC colours", | ||||
| @@ -156,45 +175,104 @@ static int export_caca(cucul_canvas_t *cv, cucul_buffer_t *ex) | |||||
| uint32_t *attrs = cv->attrs; | uint32_t *attrs = cv->attrs; | ||||
| uint32_t *chars = cv->chars; | uint32_t *chars = cv->chars; | ||||
| char *cur; | char *cur; | ||||
| uint32_t w, h; | |||||
| unsigned int n; | unsigned int n; | ||||
| /* 16 bytes for the canvas, 8 bytes for each character cell. */ | |||||
| ex->size = 16 + 8 * cv->width * cv->height; | |||||
| /* 44 bytes for the header: | |||||
| * - 4 bytes for "\xCA\xCA" + "CV" | |||||
| * - 16 bytes for the canvas header | |||||
| * - 24 bytes for the frame info | |||||
| * 8 bytes for each character cell */ | |||||
| ex->size = 44 + 8 * cv->width * cv->height; | |||||
| ex->data = malloc(ex->size); | ex->data = malloc(ex->size); | ||||
| cur = ex->data; | cur = ex->data; | ||||
| w = cv->width; | |||||
| h = cv->height; | |||||
| cur += sprintf(cur, "CACACANV%c%c%c%c%c%c%c%c", | |||||
| (unsigned char)(w >> 24), (unsigned char)((w >> 16) & 0xff), | |||||
| (unsigned char)((w >> 8) & 0xff), (unsigned char)(w & 0xff), | |||||
| (unsigned char)(h >> 24), (unsigned char)((h >> 16) & 0xff), | |||||
| (unsigned char)((h >> 8) & 0xff), (unsigned char)(h & 0xff)); | |||||
| /* magic */ | |||||
| cur += sprintf(cur, "%s", "\xCA\xCA" "CV"); | |||||
| /* canvas_header */ | |||||
| cur += sprintu32(cur, 16 + 24); | |||||
| cur += sprintu32(cur, cv->width * cv->height * 8); | |||||
| cur += sprintu16(cur, 0x0001); | |||||
| cur += sprintu32(cur, 1); | |||||
| cur += sprintu16(cur, 0x0000); | |||||
| /* frame_info */ | |||||
| cur += sprintu32(cur, cv->width); | |||||
| cur += sprintu32(cur, cv->height); | |||||
| cur += sprintu32(cur, 0); | |||||
| cur += sprintu32(cur, cv->curattr); | |||||
| cur += sprintu32(cur, 0); | |||||
| cur += sprintu32(cur, 0); | |||||
| /* canvas_data */ | |||||
| for(n = cv->height * cv->width; n--; ) | for(n = cv->height * cv->width; n--; ) | ||||
| { | { | ||||
| uint32_t ch = *chars++; | |||||
| uint32_t a = *attrs++; | |||||
| *cur++ = ch >> 24; | |||||
| *cur++ = (ch >> 16) & 0xff; | |||||
| *cur++ = (ch >> 8) & 0xff; | |||||
| *cur++ = ch & 0xff; | |||||
| *cur++ = a >> 24; | |||||
| *cur++ = (a >> 16) & 0xff; | |||||
| *cur++ = (a >> 8) & 0xff; | |||||
| *cur++ = a & 0xff; | |||||
| cur += sprintu32(cur, *chars++); | |||||
| cur += sprintu32(cur, *attrs++); | |||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* | |||||
| * The libcaca canvas format, version 1 | |||||
| * ------------------------------------ | |||||
| * | |||||
| * All types are big endian. | |||||
| * | |||||
| * struct | |||||
| * { | |||||
| * magic: | |||||
| * uint8_t caca_header[2]; // "\xCA\xCA" | |||||
| * uint8_t caca_file_type[2]; // "CV" | |||||
| * | |||||
| * canvas_header: | |||||
| * uint32_t control_size; // Control size (canvas_data - canvas_header) | |||||
| * uint32_t data_size; // Data size (EOF - canvas_data) | |||||
| * | |||||
| * uint16_t version; // Canvas format version | |||||
| * // bit 0: set to 1 if canvas is compatible | |||||
| * // with version 1 of the format | |||||
| * // bits 1-15: unused yet, must be 0 | |||||
| * | |||||
| * uint32_t frames; // Frame count | |||||
| * | |||||
| * uint16_t flags; // Feature flags | |||||
| * // bits 0-15: unused yet, must be 0 | |||||
| * | |||||
| * frame_info: | |||||
| * struct | |||||
| * { | |||||
| * uint32_t width; // Frame width | |||||
| * uint32_t height; // Frame height | |||||
| * uint32_t duration; // Frame duration in milliseconds, 0 to | |||||
| * // not specify a duration | |||||
| * uint32_t attr; // Graphics context attribute | |||||
| * int32_t handle_x; // Handle X coordinate | |||||
| * int32_t handle_y; // Handle Y coordinate | |||||
| * } | |||||
| * frame_list[frames]; | |||||
| * | |||||
| * control_extension_1: | |||||
| * control_extension_2: | |||||
| * ... | |||||
| * control_extension_N: | |||||
| * ... // reserved for future use | |||||
| * | |||||
| * canvas_data: | |||||
| * uint8_t data[data_size]; // canvas data | |||||
| * | |||||
| * data_extension_1: | |||||
| * data_extension_2: | |||||
| * ... | |||||
| * data_extension_N: | |||||
| * ... // reserved for future use | |||||
| * }; | |||||
| */ | |||||
| /* Generate UTF-8 representation of current canvas. */ | /* Generate UTF-8 representation of current canvas. */ | ||||
| static int export_utf8(cucul_canvas_t *cv, cucul_buffer_t *ex) | |||||
| static int export_utf8(cucul_canvas_t *cv, cucul_buffer_t *ex, int cr) | |||||
| { | { | ||||
| static uint8_t const palette[] = | static uint8_t const palette[] = | ||||
| { | { | ||||
| @@ -262,7 +340,7 @@ static int export_utf8(cucul_canvas_t *cv, cucul_buffer_t *ex) | |||||
| if(prevfg != 0x10 || prevbg != 0x10) | if(prevfg != 0x10 || prevbg != 0x10) | ||||
| cur += sprintf(cur, "\033[0m"); | cur += sprintf(cur, "\033[0m"); | ||||
| cur += sprintf(cur, "\n"); | |||||
| cur += sprintf(cur, cr ? "\r\n" : "\n"); | |||||
| } | } | ||||
| /* Crop to really used size */ | /* Crop to really used size */ | ||||
| @@ -22,6 +22,11 @@ | |||||
| # if defined(HAVE_ERRNO_H) | # if defined(HAVE_ERRNO_H) | ||||
| # include <errno.h> | # include <errno.h> | ||||
| # endif | # endif | ||||
| # if defined(HAVE_ARPA_INET_H) | |||||
| # include <arpa/inet.h> | |||||
| # elif defined(HAVE_NETINET_IN_H) | |||||
| # include <netinet/in.h> | |||||
| # endif | |||||
| # include <stdio.h> | # include <stdio.h> | ||||
| # include <stdlib.h> | # include <stdlib.h> | ||||
| # include <string.h> | # include <string.h> | ||||
| @@ -30,6 +35,20 @@ | |||||
| #include "cucul.h" | #include "cucul.h" | ||||
| #include "cucul_internals.h" | #include "cucul_internals.h" | ||||
| static inline uint32_t sscanu32(void const *s) | |||||
| { | |||||
| uint32_t x; | |||||
| memcpy(&x, s, 4); | |||||
| return ntohl(x); | |||||
| } | |||||
| static inline uint16_t sscanu16(void const *s) | |||||
| { | |||||
| uint16_t x; | |||||
| memcpy(&x, s, 2); | |||||
| return ntohs(x); | |||||
| } | |||||
| /* ANSI Graphic Rendition Combination Mode */ | /* ANSI Graphic Rendition Combination Mode */ | ||||
| struct ansi_grcm | struct ansi_grcm | ||||
| { | { | ||||
| @@ -85,8 +104,8 @@ cucul_canvas_t * cucul_import_canvas(cucul_buffer_t *buffer, char const *format) | |||||
| unsigned int i; | unsigned int i; | ||||
| /* If 4 first letters are CACA */ | /* If 4 first letters are CACA */ | ||||
| if(buffer->size >= 4 && | |||||
| buf[0] == 'C' && buf[1] == 'A' && buf[2] == 'C' && buf[3] != 'A') | |||||
| if(buffer->size >= 4 && (uint8_t)buf[0] == 0xca && | |||||
| (uint8_t)buf[1] == 0xca && buf[2] == 'C' && buf[3] == 'V') | |||||
| return import_caca(buffer->data, buffer->size); | return import_caca(buffer->data, buffer->size); | ||||
| /* If we find ESC[ argv, we guess it's an ANSI file */ | /* If we find ESC[ argv, we guess it's an ANSI file */ | ||||
| @@ -137,26 +156,49 @@ static cucul_canvas_t *import_caca(void const *data, unsigned int size) | |||||
| { | { | ||||
| cucul_canvas_t *cv; | cucul_canvas_t *cv; | ||||
| uint8_t const *buf = (uint8_t const *)data; | uint8_t const *buf = (uint8_t const *)data; | ||||
| unsigned int width, height, n; | |||||
| unsigned int control_size, data_size, full_size, frames, f, n; | |||||
| uint16_t version, flags; | |||||
| if(size < 16) | |||||
| if(size < 20) | |||||
| goto invalid_caca; | goto invalid_caca; | ||||
| if(buf[0] != 'C' || buf[1] != 'A' || buf[2] != 'C' || buf[3] != 'A') | |||||
| if(buf[0] != 0xca || buf[1] != 0xca || buf[2] != 'C' || buf[3] != 'V') | |||||
| goto invalid_caca; | goto invalid_caca; | ||||
| if(buf[4] != 'C' || buf[5] != 'A' || buf[6] != 'N' || buf[7] != 'V') | |||||
| control_size = sscanu32(buf + 4); | |||||
| data_size = sscanu32(buf + 8); | |||||
| version = sscanu16(buf + 12); | |||||
| frames = sscanu32(buf + 14); | |||||
| flags = sscanu16(buf + 18); | |||||
| if(size != 4 + control_size + data_size) | |||||
| goto invalid_caca; | goto invalid_caca; | ||||
| width = ((uint32_t)buf[8] << 24) | ((uint32_t)buf[9] << 16) | |||||
| | ((uint32_t)buf[10] << 8) | (uint32_t)buf[11]; | |||||
| height = ((uint32_t)buf[12] << 24) | ((uint32_t)buf[13] << 16) | |||||
| | ((uint32_t)buf[14] << 8) | (uint32_t)buf[15]; | |||||
| if(control_size < 16 + frames * 24) | |||||
| goto invalid_caca; | |||||
| if(size != 16 + width * height * 8) | |||||
| for(full_size = 0, f = 0; f < frames; f++) | |||||
| { | |||||
| unsigned int width, height, duration; | |||||
| uint32_t attr; | |||||
| int x, y; | |||||
| width = sscanu32(buf + 4 + 16 + f * 24); | |||||
| height = sscanu32(buf + 4 + 16 + f * 24 + 4); | |||||
| duration = sscanu32(buf + 4 + 16 + f * 24 + 8); | |||||
| attr = sscanu32(buf + 4 + 16 + f * 24 + 12); | |||||
| x = (int32_t)sscanu32(buf + 4 + 16 + f * 24 + 16); | |||||
| y = (int32_t)sscanu32(buf + 4 + 16 + f * 24 + 20); | |||||
| full_size += width * height * 8; | |||||
| } | |||||
| if(full_size != data_size) | |||||
| goto invalid_caca; | goto invalid_caca; | ||||
| cv = cucul_create_canvas(width, height); | |||||
| /* FIXME: read all frames, not only the first one */ | |||||
| cv = cucul_create_canvas(sscanu32(buf + 4 + 16), | |||||
| sscanu32(buf + 4 + 16 + 4)); | |||||
| if(!cv) | if(!cv) | ||||
| { | { | ||||
| @@ -166,18 +208,14 @@ static cucul_canvas_t *import_caca(void const *data, unsigned int size) | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| for(n = height * width; n--; ) | |||||
| for(n = sscanu32(buf + 4 + 16) * sscanu32(buf + 4 + 16 + 4); n--; ) | |||||
| { | { | ||||
| cv->chars[n] = ((uint32_t)buf[16 + 0 + 8 * n] << 24) | |||||
| | ((uint32_t)buf[16 + 1 + 8 * n] << 16) | |||||
| | ((uint32_t)buf[16 + 2 + 8 * n] << 8) | |||||
| | (uint32_t)buf[16 + 3 + 8 * n]; | |||||
| cv->attrs[n] = ((uint32_t)buf[16 + 4 + 8 * n] << 24) | |||||
| | ((uint32_t)buf[16 + 5 + 8 * n] << 16) | |||||
| | ((uint32_t)buf[16 + 6 + 8 * n] << 8) | |||||
| | (uint32_t)buf[16 + 7 + 8 * n]; | |||||
| cv->chars[n] = sscanu32(buf + 4 + control_size + 8 * n); | |||||
| cv->attrs[n] = sscanu32(buf + 4 + control_size + 8 * n + 4); | |||||
| } | } | ||||
| cv->curattr = sscanu32(buf + 4 + 16 + 12); | |||||
| return cv; | return cv; | ||||
| invalid_caca: | invalid_caca: | ||||
| @@ -142,7 +142,7 @@ int main(void) | |||||
| #endif | #endif | ||||
| server = malloc(sizeof(struct server)); | server = malloc(sizeof(struct server)); | ||||
| server->input = malloc(16); | |||||
| server->input = malloc(12); | |||||
| server->read = 0; | server->read = 0; | ||||
| server->client_count = 0; | server->client_count = 0; | ||||
| @@ -206,7 +206,7 @@ int main(void) | |||||
| { | { | ||||
| cucul_buffer_t *b; | cucul_buffer_t *b; | ||||
| uint8_t *buf = server->input; | uint8_t *buf = server->input; | ||||
| uint32_t width, height; | |||||
| uint32_t control_size, data_size; | |||||
| unsigned int size; | unsigned int size; | ||||
| /* Manage new connections as this function will be called sometimes | /* Manage new connections as this function will be called sometimes | ||||
| @@ -214,22 +214,23 @@ int main(void) | |||||
| manage_connections(server); | manage_connections(server); | ||||
| /* Read data from stdin */ | /* Read data from stdin */ | ||||
| read(0, buf, 16); | |||||
| read(0, buf, 12); | |||||
| while(buf[0] != 'C' || buf[1] != 'A' || buf[2] != 'C' || buf[3] != 'A') | |||||
| while(buf[0] != 0xca || buf[1] != 0xca | |||||
| || buf[2] != 'C' || buf[3] != 'V') | |||||
| { | { | ||||
| memmove(buf, buf + 1, 15); | |||||
| read(0, buf + 15, 1); | |||||
| memmove(buf, buf + 1, 11); | |||||
| read(0, buf + 11, 1); | |||||
| } | } | ||||
| width = ((uint32_t)buf[8] << 24) | ((uint32_t)buf[9] << 16) | |||||
| control_size = ((uint32_t)buf[4] << 24) | ((uint32_t)buf[5] << 16) | |||||
| | ((uint32_t)buf[6] << 8) | (uint32_t)buf[7]; | |||||
| data_size = ((uint32_t)buf[8] << 24) | ((uint32_t)buf[9] << 16) | |||||
| | ((uint32_t)buf[10] << 8) | (uint32_t)buf[11]; | | ((uint32_t)buf[10] << 8) | (uint32_t)buf[11]; | ||||
| height = ((uint32_t)buf[12] << 24) | ((uint32_t)buf[13] << 16) | |||||
| | ((uint32_t)buf[14] << 8) | (uint32_t)buf[15]; | |||||
| size = 16 + width * height * 8; | |||||
| size = 4 + control_size + data_size; | |||||
| buf = server->input = realloc(server->input, size); | buf = server->input = realloc(server->input, size); | ||||
| read(0, buf + 16, size - 16); | |||||
| read(0, buf + 12, size - 12); | |||||
| /* Free the previous canvas, if any */ | /* Free the previous canvas, if any */ | ||||
| if(server->canvas) | if(server->canvas) | ||||
| @@ -250,8 +251,8 @@ int main(void) | |||||
| } | } | ||||
| /* Get ANSI representation of the image and skip the end-of buffer | /* Get ANSI representation of the image and skip the end-of buffer | ||||
| * linefeed ("\r\n", 2 bytes) */ | |||||
| server->buffer = cucul_export_canvas(server->canvas, "utf8"); | |||||
| * linefeed ("\r\n", 2 byte) */ | |||||
| server->buffer = cucul_export_canvas(server->canvas, "utf8cr"); | |||||
| server->bufdata = cucul_get_buffer_data(server->buffer); | server->bufdata = cucul_get_buffer_data(server->buffer); | ||||
| server->buflen = cucul_get_buffer_size(server->buffer); | server->buflen = cucul_get_buffer_size(server->buffer); | ||||
| server->buflen -= 2; | server->buflen -= 2; | ||||