"Fossies" - the Fresh Open Source Software Archive 
Member "libcaca-0.99.beta20/caca/codec/import.c" (19 Oct 2021, 13906 Bytes) of package /linux/privat/libcaca-0.99.beta20.tar.bz2:
1 /*
2 * libcaca Colour ASCII-Art library
3 * Copyright © 2002—2021 Sam Hocevar <sam@hocevar.net>
4 * All Rights Reserved
5 *
6 * This library is free software. It comes without any warranty, to
7 * the extent permitted by applicable law. You can redistribute it
8 * and/or modify it under the terms of the Do What the Fuck You Want
9 * to Public License, Version 2, as published by Sam Hocevar. See
10 * http://www.wtfpl.net/ for more details.
11 */
12
13 /*
14 * This file contains various import functions.
15 */
16
17 #include "config.h"
18
19 #if !defined __KERNEL__
20 # include <stdlib.h>
21 # include <string.h>
22 # include <stdio.h>
23 #endif
24
25 #include "caca.h"
26 #include "caca_internals.h"
27 #include "codec.h"
28
29 static inline uint32_t sscanu32(void const *s)
30 {
31 uint32_t x;
32 memcpy(&x, s, 4);
33 return hton32(x);
34 }
35
36 static inline uint16_t sscanu16(void const *s)
37 {
38 uint16_t x;
39 memcpy(&x, s, 2);
40 return hton16(x);
41 }
42
43 static ssize_t import_caca(caca_canvas_t *, void const *, size_t);
44
45 /** \brief Import a memory buffer into a canvas
46 *
47 * Import a memory buffer into the given libcaca canvas's current
48 * frame. The current frame is resized accordingly and its contents are
49 * replaced with the imported data.
50 *
51 * Valid values for \c format are:
52 * - \c "": attempt to autodetect the file format.
53 * - \c "caca": import native libcaca files.
54 * - \c "text": import ASCII text files.
55 * - \c "ansi": import ANSI files.
56 * - \c "utf8": import UTF-8 files with ANSI colour codes.
57 * - \c "bin": import BIN files.
58 *
59 * The number of bytes read is returned. If the file format is valid, but
60 * not enough data was available, 0 is returned.
61 *
62 * If an error occurs, -1 is returned and \b errno is set accordingly:
63 * - \c ENOMEM Not enough memory to allocate canvas.
64 * - \c EOVERFLOW Importing data caused a value overflow.
65 * - \c EINVAL Invalid format requested.
66 *
67 * \param cv A libcaca canvas in which to import the file.
68 * \param data A memory area containing the data to be loaded into the canvas.
69 * \param len The size in bytes of the memory area.
70 * \param format A string describing the input format.
71 * \return The number of bytes read, or 0 if there was not enough data,
72 * or -1 if an error occurred.
73 */
74 ssize_t caca_import_canvas_from_memory(caca_canvas_t *cv, void const *data,
75 size_t len, char const *format)
76 {
77 if(!strcasecmp("caca", format))
78 return import_caca(cv, data, len);
79 if(!strcasecmp("utf8", format))
80 return _import_ansi(cv, data, len, 1);
81 if(!strcasecmp("text", format))
82 return _import_text(cv, data, len);
83 if(!strcasecmp("ansi", format))
84 return _import_ansi(cv, data, len, 0);
85 if(!strcasecmp("bin", format))
86 return _import_bin(cv, data, len);
87
88 /* Autodetection */
89 if(!strcasecmp("", format))
90 {
91 unsigned char const *str = data;
92 unsigned int i, j, k;
93
94 /* If 4 first bytes are 0xcaca + 'CV' */
95 if(len >= 4 && str[0] == 0xca &&
96 str[1] == 0xca && str[2] == 'C' && str[3] == 'V')
97 return import_caca(cv, data, len);
98
99 /* If we find ESC[ argv, we guess it's an ANSI file */
100 for(i = 0; i + 1 < len; i++)
101 if((str[i] == '\033') && (str[i + 1] == '['))
102 return _import_ansi(cv, data, len, 0);
103
104 /* If we find a lot of spaces at even locations,
105 * we guess it's a BIN file. */
106 for (i = j = k = 0; i < len; i += 2)
107 {
108 j += (str[i] == ' ');
109 k += (str[i + 1] == ' ');
110 }
111
112 if (j > 10 && j > len / 40 && k < 10)
113 return _import_bin(cv, data, len);
114
115 /* Otherwise, import it as text */
116 return _import_text(cv, data, len);
117 }
118
119 seterrno(EINVAL);
120 return -1;
121 }
122
123 /** \brief Import a file into a canvas
124 *
125 * Import a file into the given libcaca canvas's current frame. The
126 * current frame is resized accordingly and its contents are replaced
127 * with the imported data.
128 *
129 * Valid values for \c format are:
130 * - \c "": attempt to autodetect the file format.
131 * - \c "caca": import native libcaca files.
132 * - \c "text": import ASCII text files.
133 * - \c "ansi": import ANSI files.
134 * - \c "utf8": import UTF-8 files with ANSI colour codes.
135 * - \c "bin": import BIN files.
136 *
137 * The number of bytes read is returned. If the file format is valid, but
138 * not enough data was available, 0 is returned.
139 *
140 * If an error occurs, -1 is returned and \b errno is set accordingly:
141 * - \c ENOSYS File access is not implemented on this system.
142 * - \c ENOMEM Not enough memory to allocate canvas.
143 * - \c EINVAL Invalid format requested.
144 * caca_import_file() may also fail and set \b errno for any of the
145 * errors specified for the routine fopen().
146 *
147 * \param cv A libcaca canvas in which to import the file.
148 * \param filename The name of the file to load.
149 * \param format A string describing the input format.
150 * \return The number of bytes read, or 0 if there was not enough data,
151 * or -1 if an error occurred.
152 */
153 ssize_t caca_import_canvas_from_file(caca_canvas_t *cv, char const *filename,
154 char const *format)
155 {
156 #if defined __KERNEL__
157 seterrno(ENOSYS);
158 return -1;
159 #else
160 caca_file_t *f;
161 char *data = NULL;
162 ssize_t ret, size = 0;
163
164 f = caca_file_open(filename, "rb");
165 if(!f)
166 return -1; /* fopen already set errno */
167
168 while(!caca_file_eof(f))
169 {
170 data = realloc(data, size + 1024);
171 if(!data)
172 {
173 caca_file_close(f);
174 seterrno(ENOMEM);
175 return -1;
176 }
177
178 ret = (ssize_t)caca_file_read(f, data + size, 1024);
179 if(ret >= 0)
180 size += ret;
181 }
182 caca_file_close(f);
183
184 ret = caca_import_canvas_from_memory(cv, data, size, format);
185 free(data);
186
187 return ret;
188 #endif
189 }
190
191 /** \brief Import a memory buffer into a canvas area
192 *
193 * Import a memory buffer into the given libcaca canvas's current
194 * frame, at the specified position. For more information, see
195 * caca_import_canvas_from_memory().
196 *
197 * If an error occurs, -1 is returned and \b errno is set accordingly:
198 * - \c EINVAL Unsupported format requested or invalid coordinates.
199 * - \c ENOMEM Not enough memory to allocate canvas.
200 *
201 * \param cv A libcaca canvas in which to import the file.
202 * \param x The leftmost coordinate of the area to import to.
203 * \param y The topmost coordinate of the area to import to.
204 * \param data A memory area containing the data to be loaded into the canvas.
205 * \param len The size in bytes of the memory area.
206 * \param format A string describing the input format.
207 * \return The number of bytes read, or 0 if there was not enough data,
208 * or -1 if an error occurred.
209 */
210 ssize_t caca_import_area_from_memory(caca_canvas_t *cv, int x, int y,
211 void const *data, size_t len,
212 char const *format)
213 {
214 caca_canvas_t *tmp;
215 ssize_t ret;
216
217 tmp = caca_create_canvas(0, 0);
218 ret = caca_import_canvas_from_memory(tmp, data, len, format);
219
220 if(ret > 0)
221 caca_blit(cv, x, y, tmp, NULL);
222
223 caca_free_canvas(tmp);
224
225 return ret;
226 }
227
228 /** \brief Import a file into a canvas area
229 *
230 * Import a file into the given libcaca canvas's current frame, at the
231 * specified position. For more information, see
232 * caca_import_canvas_from_file().
233 *
234 * If an error occurs, -1 is returned and \b errno is set accordingly:
235 * - \c ENOSYS File access is not implemented on this system.
236 * - \c ENOMEM Not enough memory to allocate canvas.
237 * - \c EINVAL Unsupported format requested or invalid coordinates.
238 * caca_import_file() may also fail and set \b errno for any of the
239 * errors specified for the routine fopen().
240 *
241 * \param cv A libcaca canvas in which to import the file.
242 * \param x The leftmost coordinate of the area to import to.
243 * \param y The topmost coordinate of the area to import to.
244 * \param filename The name of the file to load.
245 * \param format A string describing the input format.
246 * \return The number of bytes read, or 0 if there was not enough data,
247 * or -1 if an error occurred.
248 */
249 ssize_t caca_import_area_from_file(caca_canvas_t *cv, int x, int y,
250 char const *filename, char const *format)
251 {
252 caca_canvas_t *tmp;
253 ssize_t ret;
254
255 tmp = caca_create_canvas(0, 0);
256 ret = caca_import_canvas_from_file(tmp, filename, format);
257
258 if(ret > 0)
259 caca_blit(cv, x, y, tmp, NULL);
260
261 caca_free_canvas(tmp);
262
263 return ret;
264 }
265
266 /** \brief Get available import formats
267 *
268 * Return a list of available import formats. The list is a NULL-terminated
269 * array of strings, interleaving a string containing the internal value for
270 * the import format, to be used with caca_import_canvas(), and a string
271 * containing the natural language description for that import format.
272 *
273 * This function never fails.
274 *
275 * \return An array of strings.
276 */
277 char const * const * caca_get_import_list(void)
278 {
279 static char const * const list[] =
280 {
281 "", "autodetect",
282 "caca", "native libcaca format",
283 "text", "plain text",
284 "ansi", "ANSI coloured text",
285 "utf8", "UTF-8 files with ANSI colour codes",
286 "bin", "BIN binary ANSI art",
287 NULL, NULL
288 };
289
290 return list;
291 }
292
293 /*
294 * XXX: the following functions are local.
295 */
296
297 static ssize_t import_caca(caca_canvas_t *cv, void const *data, size_t size)
298 {
299 uint8_t const *buf = (uint8_t const *)data;
300 size_t control_size, data_size, expected_size;
301 unsigned int frames, f, n, offset;
302 uint16_t version, flags;
303 int32_t xmin = 0, ymin = 0, xmax = 0, ymax = 0;
304
305 if(size < 20)
306 return 0;
307
308 if(buf[0] != 0xca || buf[1] != 0xca || buf[2] != 'C' || buf[3] != 'V')
309 {
310 debug("caca import error: expected \\xca\\xcaCV header");
311 goto invalid_caca;
312 }
313
314 control_size = sscanu32(buf + 4);
315 data_size = sscanu32(buf + 8);
316 version = sscanu16(buf + 12);
317 frames = sscanu32(buf + 14);
318 flags = sscanu16(buf + 18);
319
320 if(size < 4 + control_size + data_size)
321 return 0;
322
323 if(control_size < 16 + frames * 32)
324 {
325 debug("caca import error: control size %u < expected %u",
326 (unsigned int)control_size, 16 + frames * 32);
327 goto invalid_caca;
328 }
329
330 for(expected_size = 0, f = 0; f < frames; f++)
331 {
332 unsigned int width, height, duration;
333 uint32_t attr;
334 int x, y, handlex, handley;
335
336 width = sscanu32(buf + 4 + 16 + f * 32);
337 height = sscanu32(buf + 4 + 16 + f * 32 + 4);
338 duration = sscanu32(buf + 4 + 16 + f * 32 + 8);
339 attr = sscanu32(buf + 4 + 16 + f * 32 + 12);
340 x = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 16);
341 y = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 20);
342 handlex = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 24);
343 handley = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 28);
344 expected_size += width * height * 8;
345 if(-handlex < xmin)
346 xmin = -handlex;
347 if(-handley < ymin)
348 ymin = -handley;
349 if((((int32_t) width) - handlex) > xmax)
350 xmax = ((int32_t) width) - handlex;
351 if((((int32_t) height) - handley) > ymax)
352 ymax = ((int32_t) height) - handley;
353 }
354
355 if(expected_size != data_size)
356 {
357 debug("caca import error: data size %u < expected %u",
358 (unsigned int)data_size, (unsigned int)expected_size);
359 goto invalid_caca;
360 }
361
362 caca_set_canvas_size(cv, 0, 0);
363 caca_set_canvas_size(cv, xmax - xmin, ymax - ymin);
364
365 for (f = caca_get_frame_count(cv); f--; )
366 {
367 caca_free_frame(cv, f);
368 }
369
370 for (offset = 0, f = 0; f < frames; f ++)
371 {
372 unsigned int width, height;
373
374 width = sscanu32(buf + 4 + 16 + f * 32);
375 height = sscanu32(buf + 4 + 16 + f * 32 + 4);
376 caca_create_frame(cv, f);
377 caca_set_frame(cv, f);
378
379 cv->curattr = sscanu32(buf + 4 + 16 + f * 32 + 12);
380 cv->frames[f].x = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 16);
381 cv->frames[f].y = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 20);
382 cv->frames[f].handlex = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 24);
383 cv->frames[f].handley = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 28);
384
385 /* FIXME: check for return value */
386
387 for(n = width * height; n--; )
388 {
389 int x = (n % width) - cv->frames[f].handlex - xmin;
390 int y = (n / width) - cv->frames[f].handley - ymin;
391
392 caca_put_char(cv, x, y, sscanu32(buf + 4 + control_size
393 + offset + 8 * n));
394 caca_put_attr(cv, x, y, sscanu32(buf + 4 + control_size
395 + offset + 8 * n + 4));
396 }
397 offset += width * height * 8;
398
399 cv->frames[f].x -= cv->frames[f].handlex;
400 cv->frames[f].y -= cv->frames[f].handley;
401 cv->frames[f].handlex = -xmin;
402 cv->frames[f].handley = -ymin;
403 }
404
405 caca_set_frame(cv, 0);
406
407 return (ssize_t)(4 + control_size + data_size);
408
409 invalid_caca:
410 seterrno(EINVAL);
411 return -1;
412 }
413
414 ssize_t _import_bin(caca_canvas_t *cv, void const *data, size_t len)
415 {
416 uint8_t const *buf = (uint8_t const *)data;
417 size_t i;
418 int x = 0, y = 0;
419
420 caca_set_canvas_size(cv, 0, 0);
421 caca_set_canvas_size(cv, 160, len / 160);
422
423 /* Only read an even number of bytes */
424 len &= ~(size_t)1;
425
426 for (i = 0; i < len; i += 2)
427 {
428 caca_set_color_ansi(cv, buf[i + 1] & 0xf, buf[i + 1] >> 4);
429 caca_put_char(cv, x, y, caca_cp437_to_utf32(buf[i]));
430
431 ++x;
432 if (x >= 160)
433 {
434 ++y;
435 x = 0;
436 }
437 }
438
439 return len;
440 }
441