1 | /* |
---|
2 | * libcaca Colour ASCII-Art library |
---|
3 | * Copyright (c) 2002-2010 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 | |
---|
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 | * |
---|
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 | */ |
---|
72 | ssize_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 | */ |
---|
137 | ssize_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 | */ |
---|
194 | ssize_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 | */ |
---|
233 | ssize_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 | */ |
---|
261 | char 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 | |
---|
280 | static 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 | |
---|
392 | invalid_caca: |
---|
393 | seterrno(EINVAL); |
---|
394 | return -1; |
---|
395 | } |
---|
396 | |
---|
397 | /* |
---|
398 | * XXX: The following functions are aliases. |
---|
399 | */ |
---|
400 | |
---|
401 | ssize_t cucul_import_memory(cucul_canvas_t *, void const *, size_t, |
---|
402 | char const *) CACA_ALIAS(caca_import_canvas_from_memory); |
---|
403 | ssize_t cucul_import_file(cucul_canvas_t *, char const *, |
---|
404 | char const *) CACA_ALIAS(caca_import_canvas_from_file); |
---|
405 | ssize_t caca_import_memory(caca_canvas_t *, void const *, size_t, char const *) |
---|
406 | CACA_ALIAS(caca_import_canvas_from_memory); |
---|
407 | ssize_t caca_import_file(caca_canvas_t *, char const *, char const *) |
---|
408 | CACA_ALIAS(caca_import_canvas_from_file); |
---|
409 | char const * const * cucul_get_import_list(void) |
---|
410 | CACA_ALIAS(caca_get_import_list); |
---|
411 | |
---|