Fix a couple of Windows 2Gig file size issues.
[platform/upstream/flac.git] / src / share / grabbag / picture.c
1 /* grabbag - Convenience lib for various routines common to several tools
2  * Copyright (C) 2006,2007,2008,2009  Josh Coalson
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #if HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include "share/alloc.h"
24 #include "share/grabbag.h"
25 #include "FLAC/assert.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include "share/compat.h"
30
31 /* slightly different that strndup(): this always copies 'size' bytes starting from s into a NUL-terminated string. */
32 static char *local__strndup_(const char *s, size_t size)
33 {
34         char *x = safe_malloc_add_2op_(size, /*+*/1);
35         if(x) {
36                 memcpy(x, s, size);
37                 x[size] = '\0';
38         }
39         return x;
40 }
41
42 static FLAC__bool local__parse_type_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture)
43 {
44         size_t i;
45         FLAC__uint32 val = 0;
46
47         picture->type = FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
48
49         if(len == 0)
50                 return true; /* empty string implies default to 'front cover' */
51
52         for(i = 0; i < len; i++) {
53                 if(s[i] >= '0' && s[i] <= '9')
54                         val = 10*val + (FLAC__uint32)(s[i] - '0');
55                 else
56                         return false;
57         }
58
59         if(i == len)
60                 picture->type = val;
61         else
62                 return false;
63
64         return true;
65 }
66
67 static FLAC__bool local__parse_resolution_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture)
68 {
69         int state = 0;
70         size_t i;
71         FLAC__uint32 val = 0;
72
73         picture->width = picture->height = picture->depth = picture->colors = 0;
74
75         if(len == 0)
76                 return true; /* empty string implies client wants to get info from the file itself */
77
78         for(i = 0; i < len; i++) {
79                 if(s[i] == 'x') {
80                         if(state == 0)
81                                 picture->width = val;
82                         else if(state == 1)
83                                 picture->height = val;
84                         else
85                                 return false;
86                         state++;
87                         val = 0;
88                 }
89                 else if(s[i] == '/') {
90                         if(state == 2)
91                                 picture->depth = val;
92                         else
93                                 return false;
94                         state++;
95                         val = 0;
96                 }
97                 else if(s[i] >= '0' && s[i] <= '9')
98                         val = 10*val + (FLAC__uint32)(s[i] - '0');
99                 else
100                         return false;
101         }
102
103         if(state < 2)
104                 return false;
105         else if(state == 2)
106                 picture->depth = val;
107         else if(state == 3)
108                 picture->colors = val;
109         else
110                 return false;
111         if(picture->depth < 32 && 1u<<picture->depth < picture->colors)
112                 return false;
113
114         return true;
115 }
116
117 static FLAC__bool local__extract_mime_type_(FLAC__StreamMetadata *obj)
118 {
119         if(obj->data.picture.data_length >= 8 && 0 == memcmp(obj->data.picture.data, "\x89PNG\x0d\x0a\x1a\x0a", 8))
120                 return FLAC__metadata_object_picture_set_mime_type(obj, "image/png", /*copy=*/true);
121         else if(obj->data.picture.data_length >= 6 && (0 == memcmp(obj->data.picture.data, "GIF87a", 6) || 0 == memcmp(obj->data.picture.data, "GIF89a", 6)))
122                 return FLAC__metadata_object_picture_set_mime_type(obj, "image/gif", /*copy=*/true);
123         else if(obj->data.picture.data_length >= 2 && 0 == memcmp(obj->data.picture.data, "\xff\xd8", 2))
124                 return FLAC__metadata_object_picture_set_mime_type(obj, "image/jpeg", /*copy=*/true);
125         return false;
126 }
127
128 static FLAC__bool local__extract_resolution_color_info_(FLAC__StreamMetadata_Picture *picture)
129 {
130         const FLAC__byte *data = picture->data;
131         FLAC__uint32 len = picture->data_length;
132
133         if(0 == strcmp(picture->mime_type, "image/png")) {
134                 /* c.f. http://www.w3.org/TR/PNG/ */
135                 FLAC__bool need_palette = false; /* if IHDR has color_type=3, we need to also read the PLTE chunk to get the #colors */
136                 if(len < 8 || memcmp(data, "\x89PNG\x0d\x0a\x1a\x0a", 8))
137                         return false;
138                 /* try to find IHDR chunk */
139                 data += 8;
140                 len -= 8;
141                 while(len > 12) { /* every PNG chunk must be at least 12 bytes long */
142                         const FLAC__uint32 clen = (FLAC__uint32)data[0] << 24 | (FLAC__uint32)data[1] << 16 | (FLAC__uint32)data[2] << 8 | (FLAC__uint32)data[3];
143                         if(0 == memcmp(data+4, "IHDR", 4) && clen == 13) {
144                                 unsigned color_type = data[17];
145                                 picture->width = (FLAC__uint32)data[8] << 24 | (FLAC__uint32)data[9] << 16 | (FLAC__uint32)data[10] << 8 | (FLAC__uint32)data[11];
146                                 picture->height = (FLAC__uint32)data[12] << 24 | (FLAC__uint32)data[13] << 16 | (FLAC__uint32)data[14] << 8 | (FLAC__uint32)data[15];
147                                 if(color_type == 3) {
148                                         /* even though the bit depth for color_type==3 can be 1,2,4,or 8,
149                                          * the spec in 11.2.2 of http://www.w3.org/TR/PNG/ says that the
150                                          * sample depth is always 8
151                                          */
152                                         picture->depth = 8 * 3u;
153                                         need_palette = true;
154                                         data += 12 + clen;
155                                         len -= 12 + clen;
156                                 }
157                                 else {
158                                         if(color_type == 0) /* greyscale, 1 sample per pixel */
159                                                 picture->depth = (FLAC__uint32)data[16];
160                                         if(color_type == 2) /* truecolor, 3 samples per pixel */
161                                                 picture->depth = (FLAC__uint32)data[16] * 3u;
162                                         if(color_type == 4) /* greyscale+alpha, 2 samples per pixel */
163                                                 picture->depth = (FLAC__uint32)data[16] * 2u;
164                                         if(color_type == 6) /* truecolor+alpha, 4 samples per pixel */
165                                                 picture->depth = (FLAC__uint32)data[16] * 4u;
166                                         picture->colors = 0;
167                                         return true;
168                                 }
169                         }
170                         else if(need_palette && 0 == memcmp(data+4, "PLTE", 4)) {
171                                 picture->colors = clen / 3u;
172                                 return true;
173                         }
174                         else if(clen + 12 > len)
175                                 return false;
176                         else {
177                                 data += 12 + clen;
178                                 len -= 12 + clen;
179                         }
180                 }
181         }
182         else if(0 == strcmp(picture->mime_type, "image/jpeg")) {
183                 /* c.f. http://www.w3.org/Graphics/JPEG/itu-t81.pdf and Q22 of http://www.faqs.org/faqs/jpeg-faq/part1/ */
184                 if(len < 2 || memcmp(data, "\xff\xd8", 2))
185                         return false;
186                 data += 2;
187                 len -= 2;
188                 while(1) {
189                         /* look for sync FF byte */
190                         for( ; len > 0; data++, len--) {
191                                 if(*data == 0xff)
192                                         break;
193                         }
194                         if(len == 0)
195                                 return false;
196                         /* eat any extra pad FF bytes before marker */
197                         for( ; len > 0; data++, len--) {
198                                 if(*data != 0xff)
199                                         break;
200                         }
201                         if(len == 0)
202                                 return false;
203                         /* if we hit SOS or EOI, bail */
204                         if(*data == 0xda || *data == 0xd9)
205                                 return false;
206                         /* looking for some SOFn */
207                         else if(memchr("\xc0\xc1\xc2\xc3\xc5\xc6\xc7\xc9\xca\xcb\xcd\xce\xcf", *data, 13)) {
208                                 data++; len--; /* skip marker byte */
209                                 if(len < 2)
210                                         return false;
211                                 else {
212                                         const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1];
213                                         if(clen < 8 || len < clen)
214                                                 return false;
215                                         picture->width = (FLAC__uint32)data[5] << 8 | (FLAC__uint32)data[6];
216                                         picture->height = (FLAC__uint32)data[3] << 8 | (FLAC__uint32)data[4];
217                                         picture->depth = (FLAC__uint32)data[2] * (FLAC__uint32)data[7];
218                                         picture->colors = 0;
219                                         return true;
220                                 }
221                         }
222                         /* else skip it */
223                         else {
224                                 data++; len--; /* skip marker byte */
225                                 if(len < 2)
226                                         return false;
227                                 else {
228                                         const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1];
229                                         if(clen < 2 || len < clen)
230                                                 return false;
231                                         data += clen;
232                                         len -= clen;
233                                 }
234                         }
235                 }
236         }
237         else if(0 == strcmp(picture->mime_type, "image/gif")) {
238                 /* c.f. http://www.w3.org/Graphics/GIF/spec-gif89a.txt */
239                 if(len < 14)
240                         return false;
241                 if(memcmp(data, "GIF87a", 6) && memcmp(data, "GIF89a", 6))
242                         return false;
243 #if 0
244                 /* according to the GIF spec, even if the GCTF is 0, the low 3 bits should still tell the total # colors used */
245                 if(data[10] & 0x80 == 0)
246                         return false;
247 #endif
248                 picture->width = (FLAC__uint32)data[6] | ((FLAC__uint32)data[7] << 8);
249                 picture->height = (FLAC__uint32)data[8] | ((FLAC__uint32)data[9] << 8);
250 #if 0
251                 /* this value doesn't seem to be reliable... */
252                 picture->depth = (((FLAC__uint32)(data[10] & 0x70) >> 4) + 1) * 3u;
253 #else
254                 /* ...just pessimistically assume it's 24-bit color without scanning all the color tables */
255                 picture->depth = 8u * 3u;
256 #endif
257                 picture->colors = 1u << ((FLAC__uint32)(data[10] & 0x07) + 1u);
258                 return true;
259         }
260         return false;
261 }
262
263 FLAC__StreamMetadata *grabbag__picture_parse_specification(const char *spec, const char **error_message)
264 {
265         FLAC__StreamMetadata *obj;
266         int state = 0;
267         static const char *error_messages[] = {
268                 "memory allocation error",
269                 "invalid picture specification",
270                 "invalid picture specification: can't parse resolution/color part",
271                 "unable to extract resolution and color info from URL, user must set explicitly",
272                 "unable to extract resolution and color info from file, user must set explicitly",
273                 "error opening picture file",
274                 "error reading picture file",
275                 "invalid picture type",
276                 "unable to guess MIME type from file, user must set explicitly",
277                 "type 1 icon must be a 32x32 pixel PNG"
278         };
279
280         FLAC__ASSERT(0 != spec);
281         FLAC__ASSERT(0 != error_message);
282
283         /* double protection */
284         if(0 == spec)
285                 return 0;
286         if(0 == error_message)
287                 return 0;
288
289         *error_message = 0;
290
291         if(0 == (obj = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE))) {
292                 *error_message = error_messages[0];
293                 return obj;
294         }
295
296         if(strchr(spec, '|')) { /* full format */
297                 const char *p;
298                 char *q;
299                 for(p = spec; *error_message==0 && *p; ) {
300                         if(*p == '|') {
301                                 switch(state) {
302                                         case 0: /* type */
303                                                 if(!local__parse_type_(spec, p-spec, &obj->data.picture))
304                                                         *error_message = error_messages[7];
305                                                 break;
306                                         case 1: /* mime type */
307                                                 if(p-spec) { /* if blank, we'll try to guess later from the picture data */
308                                                         if(0 == (q = local__strndup_(spec, p-spec)))
309                                                                 *error_message = error_messages[0];
310                                                         else if(!FLAC__metadata_object_picture_set_mime_type(obj, q, /*copy=*/false))
311                                                                 *error_message = error_messages[0];
312                                                 }
313                                                 break;
314                                         case 2: /* description */
315                                                 if(0 == (q = local__strndup_(spec, p-spec)))
316                                                         *error_message = error_messages[0];
317                                                 else if(!FLAC__metadata_object_picture_set_description(obj, (FLAC__byte*)q, /*copy=*/false))
318                                                         *error_message = error_messages[0];
319                                                 break;
320                                         case 3: /* resolution/color (e.g. [300x300x16[/1234]] */
321                                                 if(!local__parse_resolution_(spec, p-spec, &obj->data.picture))
322                                                         *error_message = error_messages[2];
323                                                 break;
324                                         default:
325                                                 *error_message = error_messages[1];
326                                                 break;
327                                 }
328                                 p++;
329                                 spec = p;
330                                 state++;
331                         }
332                         else
333                                 p++;
334                 }
335         }
336         else { /* simple format, filename only, everything else guessed */
337                 if(!local__parse_type_("", 0, &obj->data.picture)) /* use default picture type */
338                         *error_message = error_messages[7];
339                 /* leave MIME type to be filled in later */
340                 /* leave description empty */
341                 /* leave the rest to be filled in later: */
342                 else if(!local__parse_resolution_("", 0, &obj->data.picture))
343                         *error_message = error_messages[2];
344                 else
345                         state = 4;
346         }
347
348         /* parse filename, read file, try to extract resolution/color info if needed */
349         if(*error_message == 0) {
350                 if(state != 4)
351                         *error_message = error_messages[1];
352                 else { /* 'spec' points to filename/URL */
353                         if(0 == strcmp(obj->data.picture.mime_type, "-->")) { /* magic MIME type means URL */
354                                 if(!FLAC__metadata_object_picture_set_data(obj, (FLAC__byte*)spec, strlen(spec), /*copy=*/true))
355                                         *error_message = error_messages[0];
356                                 else if(obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0)
357                                         *error_message = error_messages[3];
358                         }
359                         else { /* regular picture file */
360                                 const FLAC__off_t size = grabbag__file_get_filesize(spec);
361                                 if(size < 0)
362                                         *error_message = error_messages[5];
363                                 else {
364                                         FLAC__byte *buffer = safe_malloc_(size);
365                                         if(0 == buffer)
366                                                 *error_message = error_messages[0];
367                                         else {
368                                                 FILE *f = fopen(spec, "rb");
369                                                 if(0 == f) {
370                                                         *error_message = error_messages[5];
371                                                         free(buffer);
372                                                 }
373                                                 else {
374                                                         if(fread(buffer, 1, size, f) != (size_t)size)
375                                                                 *error_message = error_messages[6];
376                                                         fclose(f);
377                                                         if(0 == *error_message) {
378                                                                 if(!FLAC__metadata_object_picture_set_data(obj, buffer, size, /*copy=*/false))
379                                                                         *error_message = error_messages[6];
380                                                                 /* try to extract MIME type if user left it blank */
381                                                                 else if(*obj->data.picture.mime_type == '\0' && !local__extract_mime_type_(obj))
382                                                                         *error_message = error_messages[8];
383                                                                 /* try to extract resolution/color info if user left it blank */
384                                                                 else if((obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0) && !local__extract_resolution_color_info_(&obj->data.picture))
385                                                                         *error_message = error_messages[4];
386                                                         }
387                                                         else {
388                                                                 free(buffer);
389                                                         }
390                                                 }
391                                         }
392                                 }
393                         }
394                 }
395         }
396
397         if(*error_message == 0) {
398                 if(
399                         obj->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD &&
400                         (
401                                 (strcmp(obj->data.picture.mime_type, "image/png") && strcmp(obj->data.picture.mime_type, "-->")) ||
402                                 obj->data.picture.width != 32 ||
403                                 obj->data.picture.height != 32
404                         )
405                 )
406                         *error_message = error_messages[9];
407         }
408
409         if(*error_message && obj) {
410                 FLAC__metadata_object_delete(obj);
411                 obj = 0;
412         }
413
414         return obj;
415 }