1 /* (PD) 2001 The Bitzi Corporation
2 * Please see file COPYING or http://bitzi.com/publicdomain
3 * for more info.
4 *
5 * This code is based on id3v1.cpp and id3v2.cpp from FreeAmp. EMusic.com
6 * has released this code into the Public Domain.
7 * (Thanks goes to Brett Thomas, VP Engineering Emusic.com)
8 *
9 * $Id: id3.c,v 1.8 2001/08/01 21:21:56 mayhemchaos Exp $
10 */
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <assert.h>
15 #include <errno.h>
16 #ifdef WIN32
17 #include <winsock.h>
18 #else
19 #include <netinet/in.h>
20 #include <sys/param.h>
21 #endif
22
23
24 #include "id3.h"
25 #include "bitcollider.h"
26
27 #define DB printf("%s:%d\n", __FILE__, __LINE__);
28
29 /* This version 2.2 handling is not completely up to spec -- this code
30 was added to handle parsing of Apples iTunes id3v2.2 tags, which
31 are not id3v2.2 compliant. I haven't seen any other programs use
32 the old id3v2.2 stuff, so this code is tweaked to make sure that
33 iTunes files can be parsed. Yuck.
34
35 The 2.3 support is up to snuff and should work on all 2.2 compliant
36 id3v2 tags.
37 */
38 const int supportedVersion_v2_2 = 2;
39 const int supportedVersion_v2_3 = 3;
40 const unsigned frameHeaderSize_v2_3 = 10;
41 const unsigned frameHeaderSize_v2_2 = 6;
42
43 ID3Info *read_ID3v1_tag(const char* fileName, ID3Info *info);
44 ID3Info *read_ID3v2_tag(const char* fileName);
45
46 typedef struct _ID3Header
47 {
48 char tag[3];
49 unsigned char versionMajor;
50 unsigned char versionRevision;
51 unsigned char flags;
52 unsigned char size[4];
53 } ID3Header;
54 typedef struct _FrameHeader_v2_3
55 {
56 char tag[4];
57 unsigned int size;
58 unsigned short flags;
59 } FrameHeader_v2_3;
60 typedef struct _FrameHeader_v2_2
61 {
62 char tag[3];
63 unsigned char size[3];
64 } FrameHeader_v2_2;
65
66 typedef struct id3v1_0
67 {
68 char id[3];
69 char title[30];
70 char artist[30];
71 char album[30];
72 char year[4];
73 char comment[30];
74 unsigned char genre;
75
76 } id3v1_0;
77
78 typedef struct id3v1_1
79 {
80 char id[3];
81 char title[30];
82 char artist[30];
83 char album[30];
84 char year[4];
85 char comment[28];
86 char zero;
87 char track;
88 unsigned char genre;
89 } id3v1_1;
90
91 typedef struct id3v1 {
92 union {
93 struct id3v1_0 v1_0;
94 struct id3v1_1 v1_1;
95 } id3;
96 } id3v1;
97
98 #define v1_0 id3.v1_0
99 #define v1_1 id3.v1_1
100
101 static char *genreList[] =
102 {
103 "Blues",
104 "Classic Rock",
105 "Country",
106 "Dance",
107 "Disco",
108 "Funk",
109 "Grunge",
110 "Hip-Hop",
111 "Jazz",
112 "Metal",
113 "New Age",
114 "Oldies",
115 "Other",
116 "Pop",
117 "R&B",
118 "Rap",
119 "Reggae",
120 "Rock",
121 "Techno",
122 "Industrial",
123 "Alternative",
124 "Ska",
125 "Death Metal",
126 "Pranks",
127 "Soundtrack",
128 "Euro-Techno",
129 "Ambient",
130 "Trip-Hop",
131 "Vocal",
132 "Jazz+Funk",
133 "Fusion",
134 "Trance",
135 "Classical",
136 "Instrumental",
137 "Acid",
138 "House",
139 "Game",
140 "Sound Clip",
141 "Gospel",
142 "Noise",
143 "AlternRock",
144 "Bass",
145 "Soul",
146 "Punk",
147 "Space",
148 "Meditative",
149 "Instrumental Pop",
150 "Instrumental Rock",
151 "Ethnic",
152 "Gothic",
153 "Darkwave",
154 "Techno-Industrial",
155 "Electronic",
156 "Pop-Folk",
157 "Eurodance",
158 "Dream",
159 "Southern Rock",
160 "Comedy",
161 "Cult",
162 "Gangsta",
163 "Top 40",
164 "Christian Rap",
165 "Pop/Funk",
166 "Jungle",
167 "Native American",
168 "Cabaret",
169 "New Wave",
170 "Psychadelic",
171 "Rave",
172 "Showtunes",
173 "Trailer",
174 "Lo-Fi",
175 "Tribal",
176 "Acid Punk",
177 "Acid Jazz",
178 "Polka",
179 "Retro",
180 "Musical",
181 "Rock & Roll",
182 "Hard Rock",
183 "Folk",
184 "Folk-Rock",
185 "National Folk",
186 "Swing",
187 "Fast Fusion",
188 "Bebob",
189 "Latin",
190 "Revival",
191 "Celtic",
192 "Bluegrass",
193 "Avantgarde",
194 "Gothic Rock",
195 "Progressive Rock",
196 "Psychedelic Rock",
197 "Symphonic Rock",
198 "Slow Rock",
199 "Big Band",
200 "Chorus",
201 "Easy Listening",
202 "Acoustic",
203 "Humour",
204 "Speech",
205 "Chanson",
206 "Opera",
207 "Chamber Music",
208 "Sonata",
209 "Symphony",
210 "Booty Bass",
211 "Primus",
212 "Porn Groove",
213 "Satire",
214 "Slow Jam",
215 "Club",
216 "Tango",
217 "Samba",
218 "Folklore",
219 "Ballad",
220 "Power Ballad",
221 "Rhythmic Soul",
222 "Freestyle",
223 "Duet",
224 "Punk Rock",
225 "Drum Solo",
226 "Acapella",
227 "Euro-House",
228 "Dance Hall",
229 "Goa",
230 "Drum & Bass",
231 "Club-House",
232 "Hardcore",
233 "Terror",
234 "Indie",
235 "BritPop",
236 "Negerpunk",
237 "Polsk Punk",
238 "Beat",
239 "Christian Gangsta",
240 "Heavy Metal",
241 "Black Metal",
242 "Crossover",
243 "Contemporary C",
244 "Christian Rock",
245 "Merengue",
246 "Salsa",
247 "Thrash Metal",
248 "Anime",
249 "JPop",
250 "SynthPop",
251 "\0"
252 };
253
delete_ID3_tag(ID3Info * info)254 void delete_ID3_tag(ID3Info *info)
255 {
256 if (!info)
257 return;
258
259 if (info->artist)
260 free(info->artist);
261 if (info->album)
262 free(info->album);
263 if (info->title)
264 free(info->title);
265 if (info->genre)
266 free(info->genre);
267 if (info->year)
268 free(info->year);
269 if (info->encoder)
270 free(info->encoder);
271 if (info->tracknumber)
272 free(info->tracknumber);
273
274 free(info);
275 }
276
handle_frame_v2_3(char * tag,char * frameData,ID3Info * info)277 void handle_frame_v2_3(char *tag, char *frameData, ID3Info *info)
278 {
279 char tagName[5];
280
281 if (frameData == NULL || strlen(frameData) == 0)
282 return;
283
284 strncpy(tagName, tag, 4);
285 tagName[4] = 0;
286
287 if (strcmp(tagName, "TIT2") == 0)
288 info->title = strdup(frameData);
289
290 if (strcmp(tagName, "TALB") == 0)
291 info->album = strdup(frameData);
292
293 if (strcmp(tagName, "TPE1") == 0)
294 info->artist = strdup(frameData);
295
296 if (strcmp(tagName, "TYER") == 0)
297 info->year = strdup(frameData);
298
299 if (strcmp(tagName, "TCON") == 0)
300 {
301 int i;
302
303 for(i = 0;; i++)
304 {
305 if (*genreList[i] == 0)
306 break;
307
308 if (strcasecmp(genreList[i], frameData) == 0)
309 {
310 info->genre = malloc(10);
311 sprintf(info->genre, "%d", i);
312 }
313 }
314 }
315
316 if (strcmp(tagName, "TRCK") == 0)
317 info->tracknumber = strdup(frameData);
318
319 if (strcmp(tagName, "TSSE") == 0)
320 info->encoder = strdup(frameData);
321 }
322
handle_frame_v2_2(char * tag,char * frameData,ID3Info * info)323 void handle_frame_v2_2(char *tag, char *frameData, ID3Info *info)
324 {
325 char tagName[5];
326
327 if (frameData == NULL || strlen(frameData) == 0)
328 return;
329
330 strncpy(tagName, tag, 3);
331 tagName[3] = 0;
332
333 if (strcmp(tagName, "TT2") == 0)
334 info->title = strdup(frameData);
335
336 if (strcmp(tagName, "TAL") == 0)
337 info->album = strdup(frameData);
338
339 if (strcmp(tagName, "TP1") == 0)
340 info->artist = strdup(frameData);
341
342 if (strcmp(tagName, "TYE") == 0)
343 info->year = strdup(frameData);
344
345 if (strcmp(tagName, "TSI") == 0)
346 info->genre = strdup(frameData);
347
348 if (strcmp(tagName, "TRK") == 0)
349 {
350 info->tracknumber = strdup(frameData);
351 sscanf(frameData, "%[0-9]", info->tracknumber);
352 }
353
354 if (strcmp(tagName, "TSS") == 0)
355 info->encoder = strdup(frameData);
356 }
357
read_ID3_tag(const char * fileName)358 ID3Info *read_ID3_tag(const char *fileName)
359 {
360 return read_ID3v1_tag(fileName, read_ID3v2_tag(fileName));
361 }
362
read_ID3v2_tag(const char * fileName)363 ID3Info *read_ID3v2_tag(const char* fileName)
364 {
365 FILE *inFile;
366 char buffer[1024], *frameData;
367 ID3Header head;
368 FrameHeader_v2_3 frame_v2_3;
369 FrameHeader_v2_2 frame_v2_2;
370 ID3Info *info = NULL;
371 int ret;
372 unsigned int size, frameSize = 0, fileSize = 0;
373
374 inFile = fopen(fileName, "rb");
375 if (inFile == NULL)
376 return NULL;
377
378 ret = fseek(inFile, 0, SEEK_END);
379 fileSize = ftell(inFile);
380 fseek(inFile, 0, SEEK_SET);
381
382 ret = fread(&head, 1, sizeof(ID3Header), inFile);
383 if (ret != sizeof(ID3Header))
384 {
385 fclose(inFile);
386 return NULL;
387 }
388
389 if (strncmp(head.tag, "ID3", 3))
390 {
391 fclose(inFile);
392 return NULL;
393 }
394
395 if (head.versionMajor != supportedVersion_v2_2 &&
396 head.versionMajor != supportedVersion_v2_3)
397 {
398 fclose(inFile);
399 return NULL;
400 }
401 size = ( head.size[3] & 0x7F ) |
402 ((head.size[2] & 0x7F) << 7 ) |
403 ((head.size[1] & 0x7F) << 14) |
404 ((head.size[0] & 0x7F) << 21);
405
406 // Check to make sure that the size we calculate are sane!
407 if (size > fileSize)
408 {
409 fclose(inFile);
410 return NULL;
411 }
412
413 if (head.flags & (1 << 6))
414 {
415 unsigned extHeaderSize;
416
417 if (fread(&extHeaderSize, 1, sizeof(int), inFile) != sizeof(int))
418 {
419 fclose(inFile);
420 return NULL;
421 }
422 if (fread(buffer, 1, extHeaderSize, inFile) != extHeaderSize)
423 {
424 fclose(inFile);
425 return NULL;
426 }
427 }
428
429 info = malloc(sizeof(ID3Info));
430 memset(info, 0, sizeof(ID3Info));
431 for(; size > 0;)
432 {
433 if (head.versionMajor == supportedVersion_v2_2)
434 {
435 if (fread(&frame_v2_2, 1, frameHeaderSize_v2_2, inFile) !=
436 frameHeaderSize_v2_2)
437 {
438 free(info);
439 fclose(inFile);
440 return NULL;
441 }
442 ((unsigned char *)&frameSize)[0] = 0;
443 ((unsigned char *)&frameSize)[1] = frame_v2_2.size[0];
444 ((unsigned char *)&frameSize)[2] = frame_v2_2.size[1];
445 ((unsigned char *)&frameSize)[3] = frame_v2_2.size[2];
446 frameSize = ntohl(frameSize);
447 }
448 if (head.versionMajor == supportedVersion_v2_3)
449 {
450 if (fread(&frame_v2_3, 1, frameHeaderSize_v2_3, inFile) !=
451 frameHeaderSize_v2_3)
452 {
453 free(info);
454 fclose(inFile);
455 return NULL;
456 }
457 frameSize = ntohl(frame_v2_3.size);
458 }
459
460 // If the frame size is funky, skip it and move on
461 if (frameSize == 0 || frameSize > fileSize)
462 break;
463
464 frameData = malloc(frameSize + 1);
465 if (fread(frameData, 1, frameSize, inFile) != frameSize)
466 {
467 free(info);
468 free(frameData);
469 fclose(inFile);
470 return NULL;
471 }
472 frameData[frameSize] = 0;
473 if (head.versionMajor == supportedVersion_v2_2)
474 handle_frame_v2_2(frame_v2_2.tag, &frameData[1], info);
475 else
476 handle_frame_v2_3(frame_v2_3.tag, &frameData[1], info);
477
478 free(frameData);
479 size -= (head.versionMajor == supportedVersion_v2_3 ?
480 frameHeaderSize_v2_3 : frameHeaderSize_v2_2) + frameSize;
481 }
482
483 fclose(inFile);
484
485 return info;
486 }
487
remove_trailing_spaces(char * string)488 void remove_trailing_spaces(char* string)
489 {
490 char* cp = &(string[strlen(string)]);
491
492 do
493 {
494 *cp = '\0';
495 cp--;
496 }while ((*cp == ' ') && (cp >= string));
497 }
498
read_ID3v1_tag(const char * fileName,ID3Info * info)499 ID3Info *read_ID3v1_tag(const char* fileName, ID3Info *info)
500 {
501 id3v1 id3;
502 FILE *fp;
503 char buffer[31];
504
505 fp = fopen(fileName, "rb");
506 if (fp == NULL)
507 return info;
508
509 if (fseek(fp, -128, SEEK_END))
510 {
511 fclose(fp);
512 return info;
513 }
514
515 if (fread(&id3, 1, 128, fp) != 128)
516 {
517 fclose(fp);
518 return info;
519 }
520
521 if(strncmp(id3.v1_0.id, "TAG", 3))
522 {
523 fclose(fp);
524 return info;
525 }
526
527 if (info == NULL)
528 {
529 info = malloc(sizeof(ID3Info));
530 memset(info, 0, sizeof(ID3Info));
531 }
532
533 strncpy(buffer, id3.v1_0.artist, 30);
534 buffer[30] = 0;
535 remove_trailing_spaces(buffer);
536 if (strlen(buffer) && info->artist == NULL)
537 info->artist = strdup(buffer);
538
539 strncpy(buffer, id3.v1_0.album, 30);
540 buffer[30] = 0;
541 remove_trailing_spaces(buffer);
542 if (strlen(buffer) && info->album == NULL)
543 info->album = strdup(buffer);
544
545 strncpy(buffer, id3.v1_0.title, 30);
546 buffer[30] = 0;
547 remove_trailing_spaces(buffer);
548 if (strlen(buffer) && info->title == NULL)
549 info->title = strdup(buffer);
550
551 strncpy(buffer, id3.v1_0.year,4);
552 buffer[4] = 0;
553 remove_trailing_spaces(buffer);
554 if (strlen(buffer) && info->year == NULL)
555 {
556 int check;
557
558 if (sscanf(buffer, "%d", &check) == 1 && check >= 1000 && check < 3000)
559 info->year = strdup(buffer);
560 }
561
562 if( id3.v1_1.zero == 0x00 && id3.v1_1.track != 0x00)
563 {
564 sprintf(buffer, "%d", id3.v1_1.track);
565 if (strlen(buffer) && info->tracknumber == NULL)
566 info->tracknumber = strdup(buffer);
567 }
568
569 if (id3.v1_0.genre != 255)
570 {
571 sprintf(buffer, "%u", (unsigned int)id3.v1_0.genre);
572 if (strlen(buffer) && info->genre == NULL)
573 info->genre = strdup(buffer);
574 }
575
576 fclose(fp);
577 return info;
578 }
579
580