source: libcaca/trunk/caca/codec/import.c @ 4333

Last change on this file since 4333 was 4333, checked in by Sam Hocevar, 10 years ago

Large source code cleanup, getting rid of spaces, tabs, and svn keywords.

  • Property svn:keywords set to Id
File size: 13.2 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2009 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://sam.zoy.org/wtfpl/COPYING 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
29static inline uint32_t sscanu32(void const *s)
30{
31    uint32_t x;
32    memcpy(&x, s, 4);
33    return hton32(x);
34}
35
36static inline uint16_t sscanu16(void const *s)
37{
38    uint16_t x;
39    memcpy(&x, s, 2);
40    return hton16(x);
41}
42
43static 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 *
58 *  The number of bytes read is returned. If the file format is valid, but
59 *  not enough data was available, 0 is returned.
60 *
61 *  If an error occurs, -1 is returned and \b errno is set accordingly:
62 *  - \c ENOMEM Not enough memory to allocate canvas.
63 *  - \c EINVAL Invalid format requested.
64 *
65 *  \param cv A libcaca canvas in which to import the file.
66 *  \param data A memory area containing the data to be loaded into the canvas.
67 *  \param len The size in bytes of the memory area.
68 *  \param format A string describing the input format.
69 *  \return The number of bytes read, or 0 if there was not enough data,
70 *  or -1 if an error occurred.
71 */
72ssize_t caca_import_canvas_from_memory(caca_canvas_t *cv, void const *data,
73                                       size_t len, char const *format)
74{
75    if(!strcasecmp("caca", format))
76        return import_caca(cv, data, len);
77    if(!strcasecmp("utf8", format))
78        return _import_ansi(cv, data, len, 1);
79    if(!strcasecmp("text", format))
80        return _import_text(cv, data, len);
81    if(!strcasecmp("ansi", format))
82        return _import_ansi(cv, data, len, 0);
83
84    /* Autodetection */
85    if(!strcasecmp("", format))
86    {
87        unsigned char const *str = data;
88        unsigned int i;
89
90        /* If 4 first bytes are 0xcaca + 'CV' */
91        if(len >= 4 && str[0] == 0xca &&
92           str[1] == 0xca && str[2] == 'C' && str[3] == 'V')
93            return import_caca(cv, data, len);
94
95        /* If we find ESC[ argv, we guess it's an ANSI file */
96        for(i = 0; i + 1 < len; i++)
97            if((str[i] == '\033') && (str[i + 1] == '['))
98                return _import_ansi(cv, data, len, 0);
99
100        /* Otherwise, import it as text */
101        return _import_text(cv, data, len);
102    }
103
104    seterrno(EINVAL);
105    return -1;
106}
107
108/** \brief Import a file into a canvas
109 *
110 *  Import a file into the given libcaca canvas's current frame. The
111 *  current frame is resized accordingly and its contents are replaced
112 *  with the imported data.
113 *
114 *  Valid values for \c format are:
115 *  - \c "": attempt to autodetect the file format.
116 *  - \c "caca": import native libcaca files.
117 *  - \c "text": import ASCII text files.
118 *  - \c "ansi": import ANSI files.
119 *  - \c "utf8": import UTF-8 files with ANSI colour codes.
120 *
121 *  The number of bytes read is returned. If the file format is valid, but
122 *  not enough data was available, 0 is returned.
123 *
124 *  If an error occurs, -1 is returned and \b errno is set accordingly:
125 *  - \c ENOSYS File access is not implemented on this system.
126 *  - \c ENOMEM Not enough memory to allocate canvas.
127 *  - \c EINVAL Invalid format requested.
128 *  caca_import_file() may also fail and set \b errno for any of the
129 *  errors specified for the routine fopen().
130 *
131 *  \param cv A libcaca canvas in which to import the file.
132 *  \param filename The name of the file to load.
133 *  \param format A string describing the input format.
134 *  \return The number of bytes read, or 0 if there was not enough data,
135 *  or -1 if an error occurred.
136 */
137ssize_t caca_import_canvas_from_file(caca_canvas_t *cv, char const *filename,
138                                     char const *format)
139{
140#if defined __KERNEL__
141    seterrno(ENOSYS);
142    return -1;
143#else
144    caca_file_t *f;
145    char *data = NULL;
146    ssize_t ret, size = 0;
147
148    f = caca_file_open(filename, "rb");
149    if(!f)
150        return -1; /* fopen already set errno */
151
152    while(!caca_file_eof(f))
153    {
154        data = realloc(data, size + 1024);
155        if(!data)
156        {
157            caca_file_close(f);
158            seterrno(ENOMEM);
159            return -1;
160        }
161
162        ret = (ssize_t)caca_file_read(f, data + size, 1024);
163        if(ret >= 0)
164            size += ret;
165    }
166    caca_file_close(f);
167
168    ret = caca_import_canvas_from_memory(cv, data, size, format);
169    free(data);
170
171    return ret;
172#endif
173}
174
175/** \brief Import a memory buffer into a canvas area
176 *
177 *  Import a memory buffer into the given libcaca canvas's current
178 *  frame, at the specified position. For more information, see
179 *  caca_import_canvas_from_memory().
180 *
181 *  If an error occurs, -1 is returned and \b errno is set accordingly:
182 *  - \c EINVAL Unsupported format requested or invalid coordinates.
183 *  - \c ENOMEM Not enough memory to allocate canvas.
184 *
185 *  \param cv A libcaca canvas in which to import the file.
186 *  \param x The leftmost coordinate of the area to import to.
187 *  \param y The topmost coordinate of the area to import to.
188 *  \param data A memory area containing the data to be loaded into the canvas.
189 *  \param len The size in bytes of the memory area.
190 *  \param format A string describing the input format.
191 *  \return The number of bytes read, or 0 if there was not enough data,
192 *  or -1 if an error occurred.
193 */
194ssize_t caca_import_area_from_memory(caca_canvas_t *cv, int x, int y,
195                                     void const *data, size_t len,
196                                     char const *format)
197{
198    caca_canvas_t *tmp;
199    ssize_t ret;
200
201    tmp = caca_create_canvas(0, 0);
202    ret = caca_import_canvas_from_memory(tmp, data, len, format);
203
204    if(ret > 0)
205        caca_blit(cv, x, y, tmp, NULL);
206
207    caca_free_canvas(tmp);
208
209    return ret;
210}
211
212/** \brief Import a file into a canvas area
213 *
214 *  Import a file into the given libcaca canvas's current frame, at the
215 *  specified position. For more information, see
216 *  caca_import_canvas_from_file().
217 *
218 *  If an error occurs, -1 is returned and \b errno is set accordingly:
219 *  - \c ENOSYS File access is not implemented on this system.
220 *  - \c ENOMEM Not enough memory to allocate canvas.
221 *  - \c EINVAL Unsupported format requested or invalid coordinates.
222 *  caca_import_file() may also fail and set \b errno for any of the
223 *  errors specified for the routine fopen().
224 *
225 *  \param cv A libcaca canvas in which to import the file.
226 *  \param x The leftmost coordinate of the area to import to.
227 *  \param y The topmost coordinate of the area to import to.
228 *  \param filename The name of the file to load.
229 *  \param format A string describing the input format.
230 *  \return The number of bytes read, or 0 if there was not enough data,
231 *  or -1 if an error occurred.
232 */
233ssize_t caca_import_area_from_file(caca_canvas_t *cv, int x, int y,
234                                   char const *filename, char const *format)
235{
236    caca_canvas_t *tmp;
237    ssize_t ret;
238
239    tmp = caca_create_canvas(0, 0);
240    ret = caca_import_canvas_from_file(tmp, filename, format);
241
242    if(ret > 0)
243        caca_blit(cv, x, y, tmp, NULL);
244
245    caca_free_canvas(tmp);
246
247    return ret;
248}
249
250/** \brief Get available import formats
251 *
252 *  Return a list of available import formats. The list is a NULL-terminated
253 *  array of strings, interleaving a string containing the internal value for
254 *  the import format, to be used with caca_import_canvas(), and a string
255 *  containing the natural language description for that import format.
256 *
257 *  This function never fails.
258 *
259 *  \return An array of strings.
260 */
261char const * const * caca_get_import_list(void)
262{
263    static char const * const list[] =
264    {
265        "", "autodetect",
266        "caca", "native libcaca format",
267        "text", "plain text",
268        "ansi", "ANSI coloured text",
269        "utf8", "UTF-8 files with ANSI colour codes",
270        NULL, NULL
271    };
272
273    return list;
274}
275
276/*
277 * XXX: the following functions are local.
278 */
279
280static ssize_t import_caca(caca_canvas_t *cv, void const *data, size_t size)
281{
282    uint8_t const *buf = (uint8_t const *)data;
283    size_t control_size, data_size, expected_size;
284    unsigned int frames, f, n, offset;
285    uint16_t version, flags;
286    int32_t xmin = 0, ymin = 0, xmax = 0, ymax = 0;
287
288    if(size < 20)
289        return 0;
290
291    if(buf[0] != 0xca || buf[1] != 0xca || buf[2] != 'C' || buf[3] != 'V')
292    {
293        debug("caca import error: expected \\xca\\xcaCV header");
294        goto invalid_caca;
295    }
296
297    control_size = sscanu32(buf + 4);
298    data_size = sscanu32(buf + 8);
299    version = sscanu16(buf + 12);
300    frames = sscanu32(buf + 14);
301    flags = sscanu16(buf + 18);
302
303    if(size < 4 + control_size + data_size)
304        return 0;
305
306    if(control_size < 16 + frames * 32)
307    {
308        debug("caca import error: control size %u < expected %u",
309              (unsigned int)control_size, 16 + frames * 32);
310        goto invalid_caca;
311    }
312
313    for(expected_size = 0, f = 0; f < frames; f++)
314    {
315        unsigned int width, height, duration;
316        uint32_t attr;
317        int x, y, handlex, handley;
318
319        width = sscanu32(buf + 4 + 16 + f * 32);
320        height = sscanu32(buf + 4 + 16 + f * 32 + 4);
321        duration = sscanu32(buf + 4 + 16 + f * 32 + 8);
322        attr = sscanu32(buf + 4 + 16 + f * 32 + 12);
323        x = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 16);
324        y = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 20);
325        handlex = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 24);
326        handley = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 28);
327        expected_size += width * height * 8;
328        if(-handlex < xmin)
329            xmin = -handlex;
330        if(-handley < ymin)
331            ymin = -handley;
332        if((((int32_t) width) - handlex) > xmax)
333            xmax = ((int32_t) width) - handlex;
334        if((((int32_t) height) - handley) > ymax)
335            ymax = ((int32_t) height) - handley;
336    }
337
338    if(expected_size != data_size)
339    {
340        debug("caca import error: data size %u < expected %u",
341              (unsigned int)data_size, (unsigned int)expected_size);
342        goto invalid_caca;
343    }
344
345    caca_set_canvas_size(cv, 0, 0);
346    caca_set_canvas_size(cv, xmax - xmin, ymax - ymin);
347
348    for (f = caca_get_frame_count(cv); f--; )
349    {
350        caca_free_frame(cv, f);
351    }
352
353    for (offset = 0, f = 0; f < frames; f ++)
354    {
355        unsigned int width, height;
356
357        width = sscanu32(buf + 4 + 16 + f * 32);
358        height = sscanu32(buf + 4 + 16 + f * 32 + 4);
359        caca_create_frame(cv, f);
360        caca_set_frame(cv, f);
361
362        cv->curattr = sscanu32(buf + 4 + 16 + f * 32 + 12);
363        cv->frames[f].x = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 16);
364        cv->frames[f].y = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 20);
365        cv->frames[f].handlex = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 24);
366        cv->frames[f].handley = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 28);
367
368        /* FIXME: check for return value */
369
370        for(n = width * height; n--; )
371        {
372            int x = (n % width) - cv->frames[f].handlex - xmin;
373            int y = (n / width) - cv->frames[f].handley - ymin;
374
375            caca_put_char(cv, x, y, sscanu32(buf + 4 + control_size
376                                               + offset + 8 * n));
377            caca_put_attr(cv, x, y, sscanu32(buf + 4 + control_size
378                                               + offset + 8 * n + 4));
379        }
380        offset += width * height * 8;
381
382        cv->frames[f].x -= cv->frames[f].handlex;
383        cv->frames[f].y -= cv->frames[f].handley;
384        cv->frames[f].handlex = -xmin;
385        cv->frames[f].handley = -ymin;
386    }
387
388    caca_set_frame(cv, 0);
389
390    return (ssize_t)(4 + control_size + data_size);
391
392invalid_caca:
393    seterrno(EINVAL);
394    return -1;
395}
396
397/*
398 * XXX: The following functions are aliases.
399 */
400
401ssize_t cucul_import_memory(cucul_canvas_t *, void const *, size_t,
402                     char const *) CACA_ALIAS(caca_import_canvas_from_memory);
403ssize_t cucul_import_file(cucul_canvas_t *, char const *,
404                     char const *) CACA_ALIAS(caca_import_canvas_from_file);
405ssize_t caca_import_memory(caca_canvas_t *, void const *, size_t, char const *)
406                                  CACA_ALIAS(caca_import_canvas_from_memory);
407ssize_t caca_import_file(caca_canvas_t *, char const *, char const *)
408                                  CACA_ALIAS(caca_import_canvas_from_file);
409char const * const * cucul_get_import_list(void)
410         CACA_ALIAS(caca_get_import_list);
411
Note: See TracBrowser for help on using the repository browser.