* 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_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_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_html3(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)) | |||
ret = export_ansi(cv, ex); | |||
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)) | |||
ret = export_html(cv, ex); | |||
else if(!strcasecmp("html3", format)) | |||
@@ -134,6 +152,7 @@ char const * const * cucul_get_export_list(void) | |||
"caca", "native libcaca format", | |||
"ansi", "ANSI", | |||
"utf8", "UTF-8 with ANSI escape codes", | |||
"utf8cr", "UTF-8 with ANSI escape codes and MS-DOS \\r", | |||
"html", "HTML", | |||
"html3", "backwards-compatible HTML", | |||
"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 *chars = cv->chars; | |||
char *cur; | |||
uint32_t w, h; | |||
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); | |||
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--; ) | |||
{ | |||
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; | |||
} | |||
/* | |||
* 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. */ | |||
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[] = | |||
{ | |||
@@ -262,7 +340,7 @@ static int export_utf8(cucul_canvas_t *cv, cucul_buffer_t *ex) | |||
if(prevfg != 0x10 || prevbg != 0x10) | |||
cur += sprintf(cur, "\033[0m"); | |||
cur += sprintf(cur, "\n"); | |||
cur += sprintf(cur, cr ? "\r\n" : "\n"); | |||
} | |||
/* Crop to really used size */ | |||
@@ -22,6 +22,11 @@ | |||
# if defined(HAVE_ERRNO_H) | |||
# include <errno.h> | |||
# 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 <stdlib.h> | |||
# include <string.h> | |||
@@ -30,6 +35,20 @@ | |||
#include "cucul.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 */ | |||
struct ansi_grcm | |||
{ | |||
@@ -85,8 +104,8 @@ cucul_canvas_t * cucul_import_canvas(cucul_buffer_t *buffer, char const *format) | |||
unsigned int i; | |||
/* 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); | |||
/* 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; | |||
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; | |||
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; | |||
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; | |||
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; | |||
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) | |||
{ | |||
@@ -166,18 +208,14 @@ static cucul_canvas_t *import_caca(void const *data, unsigned int size) | |||
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; | |||
invalid_caca: | |||
@@ -142,7 +142,7 @@ int main(void) | |||
#endif | |||
server = malloc(sizeof(struct server)); | |||
server->input = malloc(16); | |||
server->input = malloc(12); | |||
server->read = 0; | |||
server->client_count = 0; | |||
@@ -206,7 +206,7 @@ int main(void) | |||
{ | |||
cucul_buffer_t *b; | |||
uint8_t *buf = server->input; | |||
uint32_t width, height; | |||
uint32_t control_size, data_size; | |||
unsigned int size; | |||
/* Manage new connections as this function will be called sometimes | |||
@@ -214,22 +214,23 @@ int main(void) | |||
manage_connections(server); | |||
/* 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]; | |||
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); | |||
read(0, buf + 16, size - 16); | |||
read(0, buf + 12, size - 12); | |||
/* Free the previous canvas, if any */ | |||
if(server->canvas) | |||
@@ -250,8 +251,8 @@ int main(void) | |||
} | |||
/* 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->buflen = cucul_get_buffer_size(server->buffer); | |||
server->buflen -= 2; | |||