1 | /* |
---|
2 | * libcaca Colour ASCII-Art library |
---|
3 | * Copyright (c) 2002-2009 Sam Hocevar <sam@hocevar.net> |
---|
4 | * All Rights Reserved |
---|
5 | * |
---|
6 | * $Id: import.c 3492 2009-05-21 20:54:59Z sam $ |
---|
7 | * |
---|
8 | * This library is free software. It comes without any warranty, to |
---|
9 | * the extent permitted by applicable law. You can redistribute it |
---|
10 | * and/or modify it under the terms of the Do What The Fuck You Want |
---|
11 | * To Public License, Version 2, as published by Sam Hocevar. See |
---|
12 | * http://sam.zoy.org/wtfpl/COPYING for more details. |
---|
13 | */ |
---|
14 | |
---|
15 | /* |
---|
16 | * This file contains various import functions. |
---|
17 | */ |
---|
18 | |
---|
19 | #include "config.h" |
---|
20 | |
---|
21 | #if !defined __KERNEL__ |
---|
22 | # include <stdlib.h> |
---|
23 | # include <string.h> |
---|
24 | # include <stdio.h> |
---|
25 | #endif |
---|
26 | |
---|
27 | #include "caca.h" |
---|
28 | #include "caca_internals.h" |
---|
29 | #include "codec.h" |
---|
30 | |
---|
31 | static inline uint32_t sscanu32(void const *s) |
---|
32 | { |
---|
33 | uint32_t x; |
---|
34 | memcpy(&x, s, 4); |
---|
35 | return hton32(x); |
---|
36 | } |
---|
37 | |
---|
38 | static inline uint16_t sscanu16(void const *s) |
---|
39 | { |
---|
40 | uint16_t x; |
---|
41 | memcpy(&x, s, 2); |
---|
42 | return hton16(x); |
---|
43 | } |
---|
44 | |
---|
45 | static ssize_t import_caca(caca_canvas_t *, void const *, size_t); |
---|
46 | |
---|
47 | /** \brief Import a memory buffer into a canvas |
---|
48 | * |
---|
49 | * Import a memory buffer into the given libcaca canvas's current |
---|
50 | * frame. The current frame is resized accordingly and its contents are |
---|
51 | * replaced with the imported data. |
---|
52 | * |
---|
53 | * Valid values for \c format are: |
---|
54 | * - \c "": attempt to autodetect the file format. |
---|
55 | * - \c "caca": import native libcaca files. |
---|
56 | * - \c "text": import ASCII text files. |
---|
57 | * - \c "ansi": import ANSI files. |
---|
58 | * - \c "utf8": import UTF-8 files with ANSI colour codes. |
---|
59 | * |
---|
60 | * The number of bytes read is returned. If the file format is valid, but |
---|
61 | * not enough data was available, 0 is returned. |
---|
62 | * |
---|
63 | * If an error occurs, -1 is returned and \b errno is set accordingly: |
---|
64 | * - \c ENOMEM Not enough memory to allocate canvas. |
---|
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_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 | |
---|
86 | /* Autodetection */ |
---|
87 | if(!strcasecmp("", format)) |
---|
88 | { |
---|
89 | unsigned char const *str = data; |
---|
90 | unsigned int i; |
---|
91 | |
---|
92 | /* If 4 first bytes are 0xcaca + 'CV' */ |
---|
93 | if(len >= 4 && str[0] == 0xca && |
---|
94 | str[1] == 0xca && str[2] == 'C' && str[3] == 'V') |
---|
95 | return import_caca(cv, data, len); |
---|
96 | |
---|
97 | /* If we find ESC[ argv, we guess it's an ANSI file */ |
---|
98 | for(i = 0; i + 1 < len; i++) |
---|
99 | if((str[i] == '\033') && (str[i + 1] == '[')) |
---|
100 | return _import_ansi(cv, data, len, 0); |
---|
101 | |
---|
102 | /* Otherwise, import it as text */ |
---|
103 | return _import_text(cv, data, len); |
---|
104 | } |
---|
105 | |
---|
106 | seterrno(EINVAL); |
---|
107 | return -1; |
---|
108 | } |
---|
109 | |
---|
110 | /** \brief Import a file into a canvas |
---|
111 | * |
---|
112 | * Import a file into the given libcaca canvas's current frame. The |
---|
113 | * current frame is resized accordingly and its contents are replaced |
---|
114 | * with the imported data. |
---|
115 | * |
---|
116 | * Valid values for \c format are: |
---|
117 | * - \c "": attempt to autodetect the file format. |
---|
118 | * - \c "caca": import native libcaca files. |
---|
119 | * - \c "text": import ASCII text files. |
---|
120 | * - \c "ansi": import ANSI files. |
---|
121 | * - \c "utf8": import UTF-8 files with ANSI colour codes. |
---|
122 | * |
---|
123 | * The number of bytes read is returned. If the file format is valid, but |
---|
124 | * not enough data was available, 0 is returned. |
---|
125 | * |
---|
126 | * If an error occurs, -1 is returned and \b errno is set accordingly: |
---|
127 | * - \c ENOSYS File access is not implemented on this system. |
---|
128 | * - \c ENOMEM Not enough memory to allocate canvas. |
---|
129 | * - \c EINVAL Invalid format requested. |
---|
130 | * caca_import_file() may also fail and set \b errno for any of the |
---|
131 | * errors specified for the routine fopen(). |
---|
132 | * |
---|
133 | * \param cv A libcaca canvas in which to import the file. |
---|
134 | * \param filename The name of the file to load. |
---|
135 | * \param format A string describing the input format. |
---|
136 | * \return The number of bytes read, or 0 if there was not enough data, |
---|
137 | * or -1 if an error occurred. |
---|
138 | */ |
---|
139 | ssize_t caca_import_file(caca_canvas_t *cv, char const *filename, |
---|
140 | char const *format) |
---|
141 | { |
---|
142 | #if defined __KERNEL__ |
---|
143 | seterrno(ENOSYS); |
---|
144 | return -1; |
---|
145 | #else |
---|
146 | caca_file_t *f; |
---|
147 | char *data = NULL; |
---|
148 | ssize_t ret, size = 0; |
---|
149 | |
---|
150 | f = caca_file_open(filename, "rb"); |
---|
151 | if(!f) |
---|
152 | return -1; /* fopen already set errno */ |
---|
153 | |
---|
154 | while(!caca_file_eof(f)) |
---|
155 | { |
---|
156 | data = realloc(data, size + 1024); |
---|
157 | if(!data) |
---|
158 | { |
---|
159 | caca_file_close(f); |
---|
160 | seterrno(ENOMEM); |
---|
161 | return -1; |
---|
162 | } |
---|
163 | |
---|
164 | ret = (ssize_t)caca_file_read(f, data + size, 1024); |
---|
165 | if(ret >= 0) |
---|
166 | size += ret; |
---|
167 | } |
---|
168 | caca_file_close(f); |
---|
169 | |
---|
170 | ret = caca_import_memory(cv, data, size, format); |
---|
171 | free(data); |
---|
172 | |
---|
173 | return ret; |
---|
174 | #endif |
---|
175 | } |
---|
176 | |
---|
177 | /** \brief Get available import formats |
---|
178 | * |
---|
179 | * Return a list of available import formats. The list is a NULL-terminated |
---|
180 | * array of strings, interleaving a string containing the internal value for |
---|
181 | * the import format, to be used with caca_import_canvas(), and a string |
---|
182 | * containing the natural language description for that import format. |
---|
183 | * |
---|
184 | * This function never fails. |
---|
185 | * |
---|
186 | * \return An array of strings. |
---|
187 | */ |
---|
188 | char const * const * caca_get_import_list(void) |
---|
189 | { |
---|
190 | static char const * const list[] = |
---|
191 | { |
---|
192 | "", "autodetect", |
---|
193 | "caca", "native libcaca format", |
---|
194 | "text", "plain text", |
---|
195 | "ansi", "ANSI coloured text", |
---|
196 | "utf8", "UTF-8 files with ANSI colour codes", |
---|
197 | NULL, NULL |
---|
198 | }; |
---|
199 | |
---|
200 | return list; |
---|
201 | } |
---|
202 | |
---|
203 | /* |
---|
204 | * XXX: the following functions are local. |
---|
205 | */ |
---|
206 | |
---|
207 | static ssize_t import_caca(caca_canvas_t *cv, void const *data, size_t size) |
---|
208 | { |
---|
209 | uint8_t const *buf = (uint8_t const *)data; |
---|
210 | size_t control_size, data_size, expected_size; |
---|
211 | unsigned int frames, f, n, offset; |
---|
212 | uint16_t version, flags; |
---|
213 | int32_t xmin = 0, ymin = 0, xmax = 0, ymax = 0; |
---|
214 | |
---|
215 | if(size < 20) |
---|
216 | return 0; |
---|
217 | |
---|
218 | if(buf[0] != 0xca || buf[1] != 0xca || buf[2] != 'C' || buf[3] != 'V') |
---|
219 | { |
---|
220 | debug("caca import error: expected \\xca\\xcaCV header"); |
---|
221 | goto invalid_caca; |
---|
222 | } |
---|
223 | |
---|
224 | control_size = sscanu32(buf + 4); |
---|
225 | data_size = sscanu32(buf + 8); |
---|
226 | version = sscanu16(buf + 12); |
---|
227 | frames = sscanu32(buf + 14); |
---|
228 | flags = sscanu16(buf + 18); |
---|
229 | |
---|
230 | if(size < 4 + control_size + data_size) |
---|
231 | return 0; |
---|
232 | |
---|
233 | if(control_size < 16 + frames * 32) |
---|
234 | { |
---|
235 | debug("caca import error: control size %u < expected %u", |
---|
236 | (unsigned int)control_size, 16 + frames * 32); |
---|
237 | goto invalid_caca; |
---|
238 | } |
---|
239 | |
---|
240 | for(expected_size = 0, f = 0; f < frames; f++) |
---|
241 | { |
---|
242 | unsigned int width, height, duration; |
---|
243 | uint32_t attr; |
---|
244 | int x, y, handlex, handley; |
---|
245 | |
---|
246 | width = sscanu32(buf + 4 + 16 + f * 32); |
---|
247 | height = sscanu32(buf + 4 + 16 + f * 32 + 4); |
---|
248 | duration = sscanu32(buf + 4 + 16 + f * 32 + 8); |
---|
249 | attr = sscanu32(buf + 4 + 16 + f * 32 + 12); |
---|
250 | x = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 16); |
---|
251 | y = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 20); |
---|
252 | handlex = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 24); |
---|
253 | handley = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 28); |
---|
254 | expected_size += width * height * 8; |
---|
255 | if(-handlex < xmin) |
---|
256 | xmin = -handlex; |
---|
257 | if(-handley < ymin) |
---|
258 | ymin = -handley; |
---|
259 | if((((int32_t) width) - handlex) > xmax) |
---|
260 | xmax = ((int32_t) width) - handlex; |
---|
261 | if((((int32_t) height) - handley) > ymax) |
---|
262 | ymax = ((int32_t) height) - handley; |
---|
263 | } |
---|
264 | |
---|
265 | if(expected_size != data_size) |
---|
266 | { |
---|
267 | debug("caca import error: data size %u < expected %u", |
---|
268 | (unsigned int)data_size, (unsigned int)expected_size); |
---|
269 | goto invalid_caca; |
---|
270 | } |
---|
271 | |
---|
272 | caca_set_canvas_size(cv, 0, 0); |
---|
273 | caca_set_canvas_size(cv, xmax - xmin, ymax - ymin); |
---|
274 | |
---|
275 | for (f = caca_get_frame_count(cv); f--; ) |
---|
276 | { |
---|
277 | caca_free_frame(cv, f); |
---|
278 | } |
---|
279 | |
---|
280 | for (offset = 0, f = 0; f < frames; f ++) |
---|
281 | { |
---|
282 | unsigned int width, height; |
---|
283 | |
---|
284 | width = sscanu32(buf + 4 + 16 + f * 32); |
---|
285 | height = sscanu32(buf + 4 + 16 + f * 32 + 4); |
---|
286 | caca_create_frame(cv, f); |
---|
287 | caca_set_frame(cv, f); |
---|
288 | |
---|
289 | cv->curattr = sscanu32(buf + 4 + 16 + f * 32 + 12); |
---|
290 | cv->frames[f].x = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 16); |
---|
291 | cv->frames[f].y = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 20); |
---|
292 | cv->frames[f].handlex = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 24); |
---|
293 | cv->frames[f].handley = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 28); |
---|
294 | |
---|
295 | /* FIXME: check for return value */ |
---|
296 | |
---|
297 | for(n = width * height; n--; ) |
---|
298 | { |
---|
299 | int x = (n % width) - cv->frames[f].handlex - xmin; |
---|
300 | int y = (n / width) - cv->frames[f].handley - ymin; |
---|
301 | |
---|
302 | caca_put_char(cv, x, y, sscanu32(buf + 4 + control_size |
---|
303 | + offset + 8 * n)); |
---|
304 | caca_put_attr(cv, x, y, sscanu32(buf + 4 + control_size |
---|
305 | + offset + 8 * n + 4)); |
---|
306 | } |
---|
307 | offset += width * height * 8; |
---|
308 | |
---|
309 | cv->frames[f].x -= cv->frames[f].handlex; |
---|
310 | cv->frames[f].y -= cv->frames[f].handley; |
---|
311 | cv->frames[f].handlex = -xmin; |
---|
312 | cv->frames[f].handley = -ymin; |
---|
313 | } |
---|
314 | |
---|
315 | caca_set_frame(cv, 0); |
---|
316 | |
---|
317 | return (ssize_t)(4 + control_size + data_size); |
---|
318 | |
---|
319 | invalid_caca: |
---|
320 | seterrno(EINVAL); |
---|
321 | return -1; |
---|
322 | } |
---|
323 | |
---|
324 | /* |
---|
325 | * XXX: The following functions are aliases. |
---|
326 | */ |
---|
327 | |
---|
328 | ssize_t cucul_import_memory(cucul_canvas_t *, void const *, size_t, |
---|
329 | char const *) CACA_ALIAS(caca_import_memory); |
---|
330 | ssize_t cucul_import_file(cucul_canvas_t *, char const *, |
---|
331 | char const *) CACA_ALIAS(caca_import_file); |
---|
332 | char const * const * cucul_get_import_list(void) |
---|
333 | CACA_ALIAS(caca_get_import_list); |
---|
334 | |
---|