1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdarg.h>
5 #if defined(_WIN32)
6 #    include <io.h>
7 #    define F_OK 0
8 #else
9 #    include <unistd.h>
10 #endif
11 #include "id3v2.h"
12 
13 #define NAME "id3-images"
14 
15 /*
16 
17   minimalistic program to extract cover art situated in ID3v2 tags
18 
19   copyright (c) 2015 Marc R. Schoolderman <squell@alumina.nl>
20 
21   use, modification, copying and distribution of this software is permitted
22   under the conditions described in the file 'COPYING'.
23 
24 */
25 
26 static const char* picture_types[] = {
27     "other",
28     "icon",
29     "other_icon",
30     "front_cover",
31     "back_cover",
32     "leaflet",
33     "media",
34     "lead_artist",
35     "artist",
36     "conductor",
37     "band",
38     "composer",
39     "lyricist",
40     "location",
41     "recording",
42     "performance",
43     "screencap",
44     "red_herring",
45     "illustration",
46     "logotype",
47     "studio_logotype"
48 };
49 
Help(const char * name)50 static void Help(const char *name)
51 {
52     const char *base = strchr(name, '/');
53     printf(
54         NAME " 0.2\n"
55         "Extract embedded art from ID3v2 tags to current directory\n"
56         "usage: %s filename.mp3\n"
57         "\nReport bugs to <squell@alumina.nl>.\n",
58         base? base+1 : name
59     );
60     exit(1);
61 }
62 
eprintf(const char * msg,...)63 static void eprintf(const char* msg, ...)
64 {
65     va_list args;
66     va_start(args, msg);
67     fprintf (stderr, "%s: ", NAME);
68     vfprintf (stderr, msg, args);
69     va_end(args);
70 }
71 
mime_ext(const char * fname,const char * mime_type)72 const char* mime_ext(const char *fname, const char* mime_type)
73 {
74     static const char* exts[] = {
75         "image/jpeg", ".jpg",
76         "image/png", ".png",
77         0
78     };
79     size_t i;
80     for(i=0; exts[i]; i+=2) {
81         if(strcmp(mime_type, exts[i]) == 0)
82             return exts[i+1];
83     }
84     eprintf("%s: unknown mime type: %s\n", fname, mime_type);
85     return ".unknown";
86 }
87 
write_blob(const char * basename,const char * ext,const void * blob,size_t size)88 void write_blob(const char *basename, const char* ext, const void* blob, size_t size)
89 {
90     static char image_fn[512];
91     FILE *f;
92 
93     strncpy(image_fn, basename, 510);
94     strncat(image_fn, ext, 510);
95 
96     if( access(image_fn, F_OK) == 0 ) {
97         eprintf("`%s' already exists, not overwriting\n", image_fn);
98         return;
99     }
100 
101     f = fopen(image_fn, "wb");
102     if(fwrite(blob, 1, size, f) != size | fclose(f) != 0) {
103         eprintf("`%s' could not be written\n", image_fn);
104         perror(0);
105     } else {
106         printf("%s\n", image_fn);
107     }
108 }
109 
membrk0(const char * buf,size_t size,int wide)110 const char *membrk0(const char *buf, size_t size, int wide)
111 {
112     const char* const end = buf + size - wide;
113     const int step = 1+wide;
114     for( ; buf < end; buf += step) {
115         if(!buf[0] && !buf[wide])
116             return buf;
117     }
118     return 0;
119 }
120 
main(int argc,char * argv[])121 int main(int argc, char *argv[])
122 {
123     if(argc <= 1 || argc > 2) Help(argv[0]);
124 
125     if(*++argv) {
126         void *tag = ID3_readf(*argv, 0);
127         ID3FRAME f;
128         int counter = 0;
129 
130         if(!tag) return 0;
131 
132         if(ID3_start(f,tag) >= 2) {
133             while(ID3_frame(f)) {
134                 if(strcmp(f->ID, "APIC") == 0) {
135 
136                     /* see ID3v2.3 4.15 -- 'Attached Picture' for reference */
137 
138                     char wide = f->data[0] == 1 || f->data[0] == 2;
139                     const char *mime_type = f->data+1;
140                     const char *type = memchr(mime_type, 0, f->size-(2+wide));
141                     const char *descr, *blob;
142 
143                     if(!type || (type[1]&0xFFu) > sizeof picture_types/sizeof *picture_types) {
144                         eprintf("%s has an incorrect ID3v2 tag!\n", *argv);
145                         continue;
146                     } else {
147                         ++type;          /* skip terminator */
148                     }
149 
150                     descr = type+1;
151                     blob = membrk0(descr, f->size-(descr-f->data), wide);
152                     if(!blob) {
153                         eprintf("%s has an incorrect ID3v2 tag!\n", *argv);
154                         continue;
155                     } else {
156                         blob += 1+wide;  /* skip terminator */
157                     }
158 
159                     write_blob( picture_types[*type],
160                                 mime_ext(*argv, mime_type),
161                                 blob,
162                                 f->size - (blob - f->data) );
163                     counter++;
164                 }
165             }
166         }
167 
168         if(counter == 0)
169             eprintf("%s contains no embedded images\n", *argv);
170         ID3_free(tag);
171     }
172     return 0;
173 }
174 
175