fixes based on a pass over everything with autoconf/automake
[platform/upstream/flac.git] / src / plugin_xmms / wrap_id3.c
1 /* libxmms-flac - XMMS FLAC input plugin
2  * Copyright (C) 2000,2001,2002  Josh Coalson
3  * Copyright (C) 2002  Daisuke Shimamura
4  *
5  * Based on FLAC plugin.c and mpg123 plugin
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  */
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <glib.h>
26 #include <xmms/plugin.h>
27 #include <xmms/util.h>
28 #include <xmms/configfile.h>
29 #include <xmms/titlestring.h>
30
31 #include "FLAC/metadata.h"
32
33 #include "mylocale.h"
34 #include "configure.h"
35
36 #ifdef FLAC__HAS_ID3LIB
37 #include <id3.h>
38 #include "id3_tag.h"
39
40 #else
41 #include "charset.h"
42 #include "genres.h"
43
44 typedef struct id3v1tag_t {
45         char tag[3]; /* always "TAG": defines ID3v1 tag 128 bytes before EOF */
46         char title[30];
47         char artist[30];
48         char album[30];
49         char year[4];
50         union {
51                 struct {
52                         char comment[30];
53                 } v1_0;
54                 struct {
55                         char comment[28];
56                         char __zero;
57                         unsigned char track;
58                 } v1_1;
59         } u;
60         unsigned char genre;
61 } id3v1_struct;
62
63 typedef struct id3tag_t {
64         char title[64];
65         char artist[64];
66         char album[64];
67         char comment[256];
68         char genre[256];
69         char year[16];
70         char track[16];
71 } id3v2_struct;
72
73 static gboolean local__get_id3v1_tag_as_v2_(const char *filename, id3v2_struct *tag);
74 static void local__id3v1_to_id3v2(id3v1_struct *v1, id3v2_struct *v2);
75 static const char *local__get_id3_genre(unsigned char genre_code);
76 #endif /* FLAC__HAS_ID3LIB */
77
78 static gchar *local__extname(const char *filename);
79 static char* local__getstr(char* str);
80 static int local__getnum(char* str);
81
82 static int local__vcentry_matches(const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry *entry);
83 static void local__vcentry_parse_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, gchar **dest);
84
85 /*
86  * Function flac_format_song_title (tag, filename)
87  *
88  *    Create song title according to `tag' and/or `filename' and
89  *    return it.  The title must be subsequently freed using g_free().
90  *
91  */
92 gchar *flac_format_song_title(gchar * filename)
93 {
94         gchar *ret = NULL;
95         TitleInput *input = NULL;
96         gboolean rc;
97 #ifdef FLAC__HAS_ID3LIB
98         File_Tag tag;
99 #else
100         id3v2_struct tag;
101 #endif
102         int got_vorbis_comments = 0;
103
104 #ifdef FLAC__HAS_ID3LIB
105         Initialize_File_Tag_Item (&tag);
106 #else
107         memset(&tag, 0, sizeof(tag));
108 #endif
109
110         /* first, parse vorbis comments if they exist */
111         {
112                 FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new();
113                 if(0 != iterator) {
114                         do {
115                                 if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
116                                         FLAC__StreamMetadata *block = FLAC__metadata_simple_iterator_get_block(iterator);
117                                         if(0 != block) {
118                                                 unsigned i;
119                                                 const FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment;
120                                                 for(i = 0; i < vc->num_comments; i++) {
121 #if 0
122 @@@@@
123                                                         if(local__vcentry_matches("artist", &vc->comments[i]))
124                                                                 local__vcentry_parse_value(&vc->comments[i], &tag.artist);
125                                                         else if(local__vcentry_matches("performer", &vc->comments[i]))
126                                                                 local__vcentry_parse_value(&vc->comments[i], &tag.artist);
127                                                         else if(local__vcentry_matches("album", &vc->comments[i]))
128                                                                 local__vcentry_parse_value(&vc->comments[i], &tag.album);
129                                                         else if(local__vcentry_matches("title", &vc->comments[i]))
130                                                                 local__vcentry_parse_value(&vc->comments[i], &tag.title);
131                                                         else if(local__vcentry_matches("tracknumber", &vc->comments[i]))
132                                                                 local__vcentry_parse_value(&vc->comments[i], &tag.track);
133                                                         else if(local__vcentry_matches("genre", &vc->comments[i]))
134                                                                 local__vcentry_parse_value(&vc->comments[i], &tag.genre);
135                                                         else if(local__vcentry_matches("description", &vc->comments[i]))
136                                                                 local__vcentry_parse_value(&vc->comments[i], &tag.comment);
137 #endif
138                                                 }
139                                                 FLAC__metadata_object_delete(block);
140                                                 got_vorbis_comments = 1;
141                                         }
142                                 }
143                         } while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator));
144                         FLAC__metadata_simple_iterator_delete(iterator);
145                 }
146         }
147
148         if(!got_vorbis_comments) {
149 #ifdef FLAC__HAS_ID3LIB
150                 rc = Id3tag_Read_File_Tag(filename, &tag);
151 #else
152                 rc = local__get_id3v1_tag_as_v2_(filename, &tag);
153 #endif
154         }
155
156         XMMS_NEW_TITLEINPUT(input);
157
158         if (got_vorbis_comments || rc)
159         {
160                 input->performer = local__getstr(tag.artist);
161                 input->album_name = local__getstr(tag.album);
162                 input->track_name = local__getstr(tag.title);
163                 input->track_number = local__getnum(tag.track);
164                 input->year = local__getnum(tag.year);
165                 input->genre = local__getstr(tag.genre);
166                 input->comment = local__getstr(tag.comment);
167         }
168         input->file_name = g_basename(filename);
169         input->file_path = filename;
170         input->file_ext = local__extname(filename);
171         ret = xmms_get_titlestring(flac_cfg.tag_override ? flac_cfg.tag_format : xmms_get_gentitle_format(), input);
172         g_free(input);
173
174         if (!ret)
175         {
176                 /*
177                  * Format according to filename.
178                  */
179                 ret = g_strdup(g_basename(filename));
180                 if (local__extname(ret) != NULL)
181                         *(local__extname(ret) - 1) = '\0';      /* removes period */
182         }
183
184 #ifdef FLAC__HAS_ID3LIB
185         Free_File_Tag_Item  (&tag);
186 #endif
187         return ret;
188 }
189
190 #ifndef FLAC__HAS_ID3LIB
191 /*
192  * Function get_idv2_tag_(filename, ID3v2tag)
193  *
194  *    Get ID3v2 tag from file.
195  *
196  */
197 gboolean local__get_id3v1_tag_as_v2_(const char *filename, id3v2_struct *id3v2tag)
198 {
199         FILE *file;
200         id3v1_struct id3v1tag;
201
202         memset(id3v2tag, 0, sizeof(id3v2_struct));
203
204         if ((file = fopen(filename, "rb")) != 0)
205         {
206                 if ((fseek(file, -1 * sizeof (id3v1tag), SEEK_END) == 0) &&
207                     (fread(&id3v1tag, 1, sizeof (id3v1tag), file) == sizeof (id3v1tag)) &&
208                     (strncmp(id3v1tag.tag, "TAG", 3) == 0))
209                 {
210                         local__id3v1_to_id3v2(&id3v1tag, id3v2tag);
211
212                         if (flac_cfg.convert_char_set)
213                         {
214                                 gchar *string;
215
216                                 string = convert_from_file_to_user(id3v2tag->title);
217                                 strcpy(id3v2tag->title, string);
218                                 g_free(string);
219
220                                 string = convert_from_file_to_user(id3v2tag->artist);
221                                 strcpy(id3v2tag->artist, string);
222                                 g_free(string);
223
224                                 string = convert_from_file_to_user(id3v2tag->album);
225                                 strcpy(id3v2tag->album, string);
226                                 g_free(string);
227
228                                 string = convert_from_file_to_user(id3v2tag->comment);
229                                 strcpy(id3v2tag->comment, string);
230                                 g_free(string);
231
232                                 string = convert_from_file_to_user(id3v2tag->genre);
233                                 strcpy(id3v2tag->genre, string);
234                                 g_free(string);
235
236                                 string = convert_from_file_to_user(id3v2tag->year);
237                                 strcpy(id3v2tag->year, string);
238                                 g_free(string);
239
240                                 string = convert_from_file_to_user(id3v2tag->track);
241                                 strcpy(id3v2tag->track, string);
242                                 g_free(string);
243                         }
244                 }
245
246         }
247         else
248         {
249                 return FALSE;
250         }
251
252         return TRUE;
253 }
254
255 /*
256  * Function local__id3v1_to_id3v2 (v1, v2)
257  *
258  *    Convert ID3v1 tag `v1' to ID3v2 tag `v2'.
259  *
260  */
261 void local__id3v1_to_id3v2(id3v1_struct *v1, id3v2_struct *v2)
262 {
263         memset(v2,0,sizeof(id3v2_struct));
264         strncpy(v2->title, v1->title, 30);
265         strncpy(v2->artist, v1->artist, 30);
266         strncpy(v2->album, v1->album, 30);
267         strncpy(v2->comment, v1->u.v1_0.comment, 30);
268         strncpy(v2->genre, local__get_id3_genre(v1->genre), sizeof (v2->genre));
269         strncpy(v2->year, v1->year, 4);
270
271         /* Check for v1.1 tags. */
272         if (v1->u.v1_1.__zero == 0)
273                 sprintf(v2->track, "%d", v1->u.v1_1.track);
274         else
275                 strcpy(v2->track, "0");
276
277         g_strstrip(v2->title);
278         g_strstrip(v2->artist);
279         g_strstrip(v2->album);
280         g_strstrip(v2->comment);
281         g_strstrip(v2->genre);
282         g_strstrip(v2->year);
283         g_strstrip(v2->track);
284 }
285
286 const char *local__get_id3_genre(unsigned char genre_code)
287 {
288         if (genre_code < GENRE_MAX)
289                 return gettext(id3_genres[genre_code]);
290
291         return "";
292 }
293 #endif /* ifndef FLAC__HAS_ID3LIB */
294
295 /*
296  * Function local__extname (filename)
297  *
298  *    Return pointer within filename to its extenstion, or NULL if
299  *    filename has no extension.
300  *
301  */
302 gchar *local__extname(const char *filename)
303 {
304         gchar *ext = strrchr(filename, '.');
305
306         if (ext != NULL)
307                 ++ext;
308
309         return ext;
310 }
311
312 char* local__getstr(char* str)
313 {
314         if (str && strlen(str) > 0)
315                 return str;
316         return NULL;
317 }
318
319 int local__getnum(char* str)
320 {
321         if (str && strlen(str) > 0)
322                 return atoi(str);
323         return 0;
324 }
325
326 int local__vcentry_matches(const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry *entry)
327 {
328         const FLAC__byte *eq = memchr(entry->entry, '=', entry->length);
329         const unsigned field_name_length = strlen(field_name);
330         return (0 != eq && (unsigned)(eq-entry->entry) == field_name_length && 0 == strncasecmp(field_name, entry->entry, field_name_length));
331 }
332
333 void local__vcentry_parse_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, gchar **dest)
334 {
335         const FLAC__byte *eq = memchr(entry->entry, '=', entry->length);
336
337         if(0 == eq)
338                 return;
339         else {
340                 const unsigned value_length = entry->length - (unsigned)((++eq) - entry->entry);
341
342                 *dest = g_malloc(value_length + 1);
343                 if(0 != *dest) {
344                         memcpy(*dest, eq, value_length);
345                         (*dest)[value_length] = '\0';
346                 }
347         }
348 }