* 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; | ||||