"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:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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