initial import
[flac.git] / src / plugin_common / id3v2.c
1 /* plugin_common - Routines common to several plugins
2  * Copyright (C) 2002  Daisuke Shimamura
3  *
4  * Almost from id3_tag.c - 2001/02/16
5  *  EasyTAG - Tag editor for MP3 and OGG files
6  *  Copyright (C) 2001-2002  Jerome Couderc <j.couderc@ifrance.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 #include <id3.h>
24 #include <gtk/gtk.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <unistd.h>
30
31 #include "configure.h"
32 #include "genres.h"
33 #include "charset.h"
34 #include "mylocale.h"
35 #include "id3_tag.h"
36
37 /****************
38  * Declarations *
39  ****************/
40 #define ID3V2_MAX_STRING_LEN 4096
41 #define NUMBER_TRACK_FORMATED 1
42
43 /**************
44  * Prototypes *
45  **************/
46 static size_t ID3Tag_Link_1       (ID3Tag *id3tag, const char *filename);
47 static size_t ID3Field_GetASCII_1 (const ID3Field *field, char *buffer, size_t maxChars, index_t itemNum);
48
49 static gchar  *Id3tag_Genre_To_String (unsigned char genre_code);
50 static void Strip_String (gchar *string);
51
52 @@@@
53
54 /*************
55  * Functions *
56  *************/
57 /*
58  * Read id3v1.x / id3v2 tag and load data into the File_Tag structure using id3lib functions.
59  * Returns TRUE on success, else FALSE.
60  * If a tag entry exists (ex: title), we allocate memory, else value stays to NULL
61  */
62 gboolean Id3tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
63 {
64         FILE *file;
65         ID3Tag *id3_tag = NULL;    /* Tag defined by the id3lib */
66         gchar *string, *string1, *string2;
67         gboolean USE_CHARACTER_SET_TRANSLATION;
68
69         USE_CHARACTER_SET_TRANSLATION = flac_cfg.convert_char_set;
70
71         if (!filename || !FileTag)
72                 return FALSE;
73
74         if ( (file=fopen(filename,"r"))==NULL )
75         {
76                 g_print(_("ERROR while opening file: '%s' (%s).\n\a"),filename,g_strerror(errno));
77                 return FALSE;
78         }
79         fclose(file); // We close it cause id3lib opens/closes file itself
80
81
82         /* Get data from tag */
83         if ( (id3_tag = ID3Tag_New()) )
84         {
85                 ID3Frame *id3_frame;
86                 ID3Field *id3_field;
87                 luint frm_size;
88                 luint num_chars;
89                 guint field_num = 0; // First field
90
91                 /* Link the file to the tag */
92                 frm_size = ID3Tag_Link_1(id3_tag,filename);
93
94                 string = g_malloc(ID3V2_MAX_STRING_LEN+1);
95
96                 /*********
97                  * Title *
98                  *********/
99                 if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TITLE)) )
100                 {
101                         if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
102                         {
103                                 // Note: if 'num_chars' is equal to 0, then the field is empty or corrupted!
104                                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
105                                          && string != NULL )
106                                 {
107                                         if (USE_CHARACTER_SET_TRANSLATION)
108                                         {
109                                                 string1 = convert_from_file_to_user(string);
110                                                 Strip_String(string1);
111                                                 FileTag->title = g_strdup(string1);
112                                                 g_free(string1);
113                                         }else
114                                         {
115                                                 Strip_String(string);
116                                                 FileTag->title = g_strdup(string);
117                                         }
118                                 }
119                         }
120                 }
121
122
123                 /**********
124                  * Artist *
125                  **********/
126                 if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_LEADARTIST)) )
127                 {
128                         if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
129                         {
130                                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
131                                          && string != NULL )
132                                 {
133                                         if (USE_CHARACTER_SET_TRANSLATION)
134                                         {
135                                                 string1 = convert_from_file_to_user(string);
136                                                 Strip_String(string1);
137                                                 FileTag->artist = g_strdup(string1);
138                                                 g_free(string1);
139                                         }else
140                                         {
141                                                 Strip_String(string);
142                                                 FileTag->artist = g_strdup(string);
143                                         }
144                                 }
145                         }
146                 }
147
148
149                 /*********
150                  * Album *
151                  *********/
152                 if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ALBUM)) )
153                 {
154                         if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
155                         {
156                                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
157                                          && string != NULL )
158                                 {
159                                         if (USE_CHARACTER_SET_TRANSLATION)
160                                         {
161                                                 string1 = convert_from_file_to_user(string);
162                                                 Strip_String(string1);
163                                                 FileTag->album = g_strdup(string1);
164                                                 g_free(string1);
165                                         }else
166                                         {
167                                                 Strip_String(string);
168                                                 FileTag->album = g_strdup(string);
169                                         }
170                                 }
171                         }
172                 }
173
174
175                 /********
176                  * Year *
177                  ********/
178                 if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_YEAR)) )
179                 {
180                         if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
181                         {
182                                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
183                                          && string != NULL )
184                                 {
185                                         gchar *tmp_str;
186
187                                         Strip_String(string);
188
189                                         /* Fix for id3lib 3.7.x: if the id3v1.x tag was filled with spaces
190                                          * instead of zeroes, then the year field contains garbages! */
191                                         tmp_str = string;
192                                         while (isdigit(*tmp_str)) tmp_str++;
193                                         *tmp_str = 0;
194                                         /* End of fix for id3lib 3.7.x */
195
196                                         if (USE_CHARACTER_SET_TRANSLATION)
197                                         {
198                                                 string1 = convert_from_file_to_user(string);
199                                                 Strip_String(string1);
200                                                 FileTag->year = g_strdup(string1);
201                                                 g_free(string1);
202                                         }else
203                                         {
204                                                 Strip_String(string);
205                                                 FileTag->year = g_strdup(string);
206                                         }
207                                 }
208                         }
209                 }
210
211
212                 /*************************
213                  * Track and Total Track *
214                  *************************/
215                 if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TRACKNUM)) )
216                 {
217                         if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
218                         {
219                                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
220                                          && string != NULL )
221                                 {
222
223                                         Strip_String(string);
224
225                                         if (USE_CHARACTER_SET_TRANSLATION)
226                                         {
227                                                 string1 = convert_from_file_to_user(string);
228                                                 string2 = strchr(string1,'/');
229                                                 if (NUMBER_TRACK_FORMATED)
230                                                 {
231                                                         if (string2)
232                                                         {
233                                                                 FileTag->track_total = g_strdup_printf("%.2d",atoi(string2+1)); // Just to have numbers like this : '01', '05', '12', ...
234                                                                 *string2 = '\0';
235                                                         }
236                                                         FileTag->track = g_strdup_printf("%.2d",atoi(string1)); // Just to have numbers like this : '01', '05', '12', ...
237                                                 }else
238                                                 {
239                                                         if (string2)
240                                                         {
241                                                                 FileTag->track_total = g_strdup(string2+1);
242                                                                 *string2 = '\0';
243                                                         }
244                                                         FileTag->track = g_strdup(string1);
245                                                 }
246                                                 g_free(string1);
247                                         }else
248                                         {
249                                                 string2 = strchr(string,'/');
250                                                 if (NUMBER_TRACK_FORMATED)
251                                                 {
252                                                         if (string2)
253                                                         {
254                                                                 FileTag->track_total = g_strdup_printf("%.2d",atoi(string2+1)); // Just to have numbers like this : '01', '05', '12', ...
255                                                                 *string2 = '\0';
256                                                         }
257                                                         FileTag->track = g_strdup_printf("%.2d",atoi(string)); // Just to have numbers like this : '01', '05', '12', ...
258                                                 }else
259                                                         {
260                                                         if (string2)
261                                                         {
262                                                                 FileTag->track_total = g_strdup(string2+1);
263                                                                 *string2 = '\0';
264                                                         }
265                                                         FileTag->track = g_strdup(string);
266                                                 }
267                                         }
268                                 }
269                         }
270                 }
271
272
273                 /*********
274                  * Genre *
275                  *********/
276                 if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_CONTENTTYPE)) )
277                 {
278                         if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
279                         {
280                                 /*
281                                  * We manipulate only the name of the genre
282                                  */
283                                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
284                                          && string != NULL )
285                                 {
286                                         gchar *tmp;
287
288                                         Strip_String(string);
289
290                                         if ( (string[0]=='(') && (tmp=strchr(string,')')) && (strlen((tmp+1))>0) )
291                                         {
292
293                                                 /* Convert a genre written as '(3)Dance' into 'Dance' */
294                                                 if (USE_CHARACTER_SET_TRANSLATION)
295                                                 {
296                                                         string1 = convert_from_file_to_user(tmp+1);
297                                                         FileTag->genre = g_strdup(string1);
298                                                         g_free(string1);
299                                                 }else
300                                                 {
301                                                         FileTag->genre = g_strdup(tmp+1);
302                                                 }
303
304                                         }else if ( (string[0]=='(') && (tmp=strchr(string,')')) )
305                                         {
306
307                                                 /* Convert a genre written as '(3)' into 'Dance' */
308                                                 *tmp = 0;
309                                                 if (USE_CHARACTER_SET_TRANSLATION)
310                                                 {
311                                                         string1 = convert_from_file_to_user(Id3tag_Genre_To_String(atoi(string+1)));
312                                                         FileTag->genre = g_strdup(string1);
313                                                         g_free(string1);
314                                                 }else
315                                                 {
316                                                         FileTag->genre = g_strdup(Id3tag_Genre_To_String(atoi(string+1)));
317                                                 }
318
319                                         }else
320                                         {
321
322                                                 /* Genre is already written as 'Dance' */
323                                                         if (USE_CHARACTER_SET_TRANSLATION)
324                                                 {
325                                                         string1 = convert_from_file_to_user(string);
326                                                         FileTag->genre = g_strdup(string1);
327                                                         g_free(string1);
328                                                 }else
329                                                 {
330                                                         FileTag->genre = g_strdup(string);
331                                                 }
332
333                                         }
334                                 }
335                         }
336                 }
337
338
339                 /***********
340                  * Comment *
341                  ***********/
342                 if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COMMENT)) )
343                 {
344                         if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
345                         {
346                                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
347                                          && string != NULL )
348                                 {
349                                         if (USE_CHARACTER_SET_TRANSLATION)
350                                         {
351                                                 string1 = convert_from_file_to_user(string);
352                                                 Strip_String(string1);
353                                                 FileTag->comment = g_strdup(string1);
354                                                 g_free(string1);
355                                         }else
356                                         {
357                                                 Strip_String(string);
358                                                 FileTag->comment = g_strdup(string);
359                                         }
360                                 }
361                         }
362                         /*if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_DESCRIPTION)) )
363                         {
364                                 gchar *comment1 = g_malloc0(MAX_STRING_LEN+1);
365                                 num_chars = ID3Field_GetASCII(id3_field,comment1,MAX_STRING_LEN,Item_Num);
366                                 g_free(comment1);
367                         }
368                         if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_LANGUAGE)) )
369                         {
370                                 gchar *comment2 = g_malloc0(MAX_STRING_LEN+1);
371                                 num_chars = ID3Field_GetASCII(id3_field,comment2,MAX_STRING_LEN,Item_Num);
372                                 g_free(comment2);
373                         }*/
374                 }
375                 g_free(string);
376
377                 /* Free allocated data */
378                 ID3Tag_Delete(id3_tag);
379         }
380
381         return TRUE;
382 }
383
384 /*
385  * Returns the name of a genre code if found
386  * Three states for genre code :
387  *    - defined (0 to GENRE_MAX)
388  *    - undefined/unknown (GENRE_MAX+1 to ID3_INVALID_GENRE-1)
389  *    - invalid (>ID3_INVALID_GENRE)
390  */
391 static gchar *Id3tag_Genre_To_String (unsigned char genre_code)
392 {
393         if (genre_code>=ID3_INVALID_GENRE)    /* empty */
394                 return "";
395         else if (genre_code>GENRE_MAX)        /* unknown tag */
396                 return "Unknown";
397         else                                  /* known tag */
398                 return id3_genres[genre_code];
399 }
400
401
402
403 /*
404  * As the ID3Tag_Link function of id3lib-3.8.0pre2 returns the ID3v1 tags
405  * when a file has both ID3v1 and ID3v2 tags, we first try to explicitely
406  * get the ID3v2 tags with ID3Tag_LinkWithFlags and, if we cannot get them,
407  * fall back to the ID3v1 tags.
408  * (Written by Holger Schemel).
409  */
410 static size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename)
411 {
412         size_t offset;
413
414 #   if ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8)  )
415                 /* First, try to get the ID3v2 tags */
416                 offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V2);
417                 if (offset == 0)
418                 {
419                         /* No ID3v2 tags available => try to get the ID3v1 tags */
420                         offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1);
421                 }
422 #   else
423                 /* Function 'ID3Tag_LinkWithFlags' is not defined up to id3lib-.3.7.13 */
424                 offset = ID3Tag_Link(id3tag,filename);
425 #   endif
426         //g_print("ID3 TAG SIZE: %d\t%s\n",offset,g_basename(filename));
427         return offset;
428 }
429
430
431 /*
432  * As the ID3Field_GetASCII function differs with the version of id3lib, we must redefine it.
433  */
434 static size_t ID3Field_GetASCII_1(const ID3Field *field, char *buffer, size_t maxChars, index_t itemNum)
435 {
436
437         /* Defined by id3lib:   ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION
438          * Defined by autoconf: ID3LIB_MAJOR,         ID3LIB_MINOR,         ID3LIB_PATCH
439          *
440          * <= 3.7.12 : first item num is 1 for ID3Field_GetASCII
441          *  = 3.7.13 : first item num is 0 for ID3Field_GetASCII
442          * >= 3.8.0  : doesn't need item num for ID3Field_GetASCII
443          */
444          //g_print("id3lib version: %d.%d.%d\n",ID3LIB_MAJOR,ID3LIB_MINOR,ID3LIB_PATCH);
445 #    if (ID3LIB_MAJOR >= 3)
446                  // (>= 3.x.x)
447 #        if (ID3LIB_MINOR <= 7)
448                          // (3.0.0 to 3.7.x)
449 #            if (ID3LIB_PATCH >= 13)
450                                  // (>= 3.7.13)
451                                  return ID3Field_GetASCII(field,buffer,maxChars,itemNum);
452 #            else
453                                  return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1);
454 #            endif
455 #        else
456                          // (>= to 3.8.0)
457                          //return ID3Field_GetASCII(field,buffer,maxChars);
458                          return ID3Field_GetASCIIItem(field,buffer,maxChars,itemNum);
459 #        endif
460 #    else
461                  // Not tested (< 3.x.x)
462                  return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1);
463 #    endif
464 }
465
466 /*
467  * Delete spaces at the end and the beginning of the string
468  */
469 static void Strip_String (gchar *string)
470 {
471         if (!string) return;
472         string = g_strstrip(string);
473 }