]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/ext/lame/id3tag.c
8f64df4bca0103a626ec8f8c5bf6c01b97e89778
[16.git] / src / lib / doslib / ext / lame / id3tag.c
1 /*
2  * id3tag.c -- Write ID3 version 1 and 2 tags.
3  *
4  * Copyright (C) 2000 Don Melton
5  * Copyright (C) 2011 Robert Hegemann
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 /*
23  * HISTORY: This source file is part of LAME (see http://www.mp3dev.org)
24  * and was originally adapted by Conrad Sanderson <c.sanderson@me.gu.edu.au>
25  * from mp3info by Ricardo Cerqueira <rmc@rccn.net> to write only ID3 version 1
26  * tags.  Don Melton <don@blivet.com> COMPLETELY rewrote it to support version
27  * 2 tags and be more conformant to other standards while remaining flexible.
28  *
29  * NOTE: See http://id3.org/ for more information about ID3 tag formats.
30  */
31
32 /* $Id: id3tag.c,v 1.75.2.1 2011/11/26 18:15:27 robert Exp $ */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #ifdef STDC_HEADERS
39 # include <stddef.h>
40 # include <stdlib.h>
41 # include <string.h>
42 # include <ctype.h>
43 #else
44 # ifndef HAVE_STRCHR
45 #  define strchr index
46 #  define strrchr rindex
47 # endif
48 char   *strchr(), *strrchr();
49 # ifndef HAVE_MEMCPY
50 #  define memcpy(d, s, n) bcopy ((s), (d), (n))
51 # endif
52 #endif
53
54
55 #include "lame.h"
56 #include "machine.h"
57 #include "encoder.h"
58 #include "id3tag.h"
59 #include "lame_global_flags.h"
60 #include "util.h"
61 #include "bitstream.h"
62
63
64 static const char *const genre_names[] = {
65     /*
66      * NOTE: The spelling of these genre names is identical to those found in
67      * Winamp and mp3info.
68      */
69     "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
70     "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B",
71     "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
72     "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
73     "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
74     "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock",
75     "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
76     "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
77     "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
78     "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
79     "Native US", "Cabaret", "New Wave", "Psychedelic", "Rave",
80     "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz",
81     "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
82     "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin",
83     "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
84     "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
85     "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech",
86     "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass",
87     "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
88     "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
89     "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall",
90     "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie",
91     "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta",
92     "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian",
93     "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
94     "SynthPop"
95 };
96
97 #define GENRE_NAME_COUNT \
98     ((int)(sizeof genre_names / sizeof (const char *const)))
99
100 static const int genre_alpha_map[] = {
101     123, 34, 74, 73, 99, 20, 40, 26, 145, 90, 116, 41, 135, 85, 96, 138, 89, 0,
102     107, 132, 65, 88, 104, 102, 97, 136, 61, 141, 32, 1, 112, 128, 57, 140, 2,
103     139, 58, 3, 125, 50, 22, 4, 55, 127, 122, 120, 98, 52, 48, 54, 124, 25, 84,
104     80, 115, 81, 119, 5, 30, 36, 59, 126, 38, 49, 91, 6, 129, 79, 137, 7, 35,
105     100, 131, 19, 33, 46, 47, 8, 29, 146, 63, 86, 71, 45, 142, 9, 77, 82, 64,
106     133, 10, 66, 39, 11, 103, 12, 75, 134, 13, 53, 62, 109, 117, 23, 108, 92,
107     67, 93, 43, 121, 15, 68, 14, 16, 76, 87, 118, 17, 78, 143, 114, 110, 69, 21,
108     111, 95, 105, 42, 37, 24, 56, 44, 101, 83, 94, 106, 147, 113, 18, 51, 130,
109     144, 60, 70, 31, 72, 27, 28
110 };
111
112 #define GENRE_ALPHA_COUNT ((int)(sizeof genre_alpha_map / sizeof (int)))
113
114 #define GENRE_INDEX_OTHER 12
115
116
117 #define FRAME_ID(a, b, c, d) \
118     ( ((unsigned long)(a) << 24) \
119     | ((unsigned long)(b) << 16) \
120     | ((unsigned long)(c) <<  8) \
121     | ((unsigned long)(d) <<  0) )
122
123 typedef enum UsualStringIDs { ID_TITLE = FRAME_ID('T', 'I', 'T', '2')
124         , ID_ARTIST = FRAME_ID('T', 'P', 'E', '1')
125         , ID_ALBUM = FRAME_ID('T', 'A', 'L', 'B')
126         , ID_GENRE = FRAME_ID('T', 'C', 'O', 'N')
127         , ID_ENCODER = FRAME_ID('T', 'S', 'S', 'E')
128         , ID_PLAYLENGTH = FRAME_ID('T', 'L', 'E', 'N')
129         , ID_COMMENT = FRAME_ID('C', 'O', 'M', 'M') /* full text string */
130 } UsualStringIDs;
131
132 typedef enum NumericStringIDs { ID_DATE = FRAME_ID('T', 'D', 'A', 'T') /* "ddMM" */
133         , ID_TIME = FRAME_ID('T', 'I', 'M', 'E') /* "hhmm" */
134         , ID_TPOS = FRAME_ID('T', 'P', 'O', 'S') /* '0'-'9' and '/' allowed */
135         , ID_TRACK = FRAME_ID('T', 'R', 'C', 'K') /* '0'-'9' and '/' allowed */
136         , ID_YEAR = FRAME_ID('T', 'Y', 'E', 'R') /* "yyyy" */
137 } NumericStringIDs;
138
139 typedef enum MiscIDs { ID_TXXX = FRAME_ID('T', 'X', 'X', 'X')
140         , ID_WXXX = FRAME_ID('W', 'X', 'X', 'X')
141         , ID_SYLT = FRAME_ID('S', 'Y', 'L', 'T')
142         , ID_APIC = FRAME_ID('A', 'P', 'I', 'C')
143         , ID_GEOB = FRAME_ID('G', 'E', 'O', 'B')
144         , ID_PCNT = FRAME_ID('P', 'C', 'N', 'T')
145         , ID_AENC = FRAME_ID('A', 'E', 'N', 'C')
146         , ID_LINK = FRAME_ID('L', 'I', 'N', 'K')
147         , ID_ENCR = FRAME_ID('E', 'N', 'C', 'R')
148         , ID_GRID = FRAME_ID('G', 'R', 'I', 'D')
149         , ID_PRIV = FRAME_ID('P', 'R', 'I', 'V')
150         , ID_VSLT = FRAME_ID('V', 'S', 'L', 'T') /* full text string */
151 } MiscIDs;
152
153
154 static int
155 frame_id_matches(int id, int mask)
156 {
157     int     result = 0, i, window = 0xff;
158     for (i = 0; i < 4; ++i, window <<= 8) {
159         int const mw = (mask & window);
160         int const iw = (id & window);
161         if (mw != 0 && mw != iw) {
162             result |= iw;
163         }
164     }
165     return result;
166 }
167
168 static int
169 isFrameIdMatching(int id, int mask)
170 {
171     return frame_id_matches(id, mask) == 0 ? 1 : 0;
172 }
173
174 static int
175 test_tag_spec_flags(lame_internal_flags const *gfc, unsigned int tst)
176 {
177     return (gfc->tag_spec.flags & tst) != 0u ? 1 : 0;
178 }
179
180 #if 0
181 static void
182 debug_tag_spec_flags(lame_internal_flags * gfc, const char* info)
183 {
184     MSGF(gfc, "%s\n", info);
185     MSGF(gfc, "CHANGED_FLAG  : %d\n", test_tag_spec_flags(gfc, CHANGED_FLAG )); 
186     MSGF(gfc, "ADD_V2_FLAG   : %d\n", test_tag_spec_flags(gfc, ADD_V2_FLAG  )); 
187     MSGF(gfc, "V1_ONLY_FLAG  : %d\n", test_tag_spec_flags(gfc, V1_ONLY_FLAG )); 
188     MSGF(gfc, "V2_ONLY_FLAG  : %d\n", test_tag_spec_flags(gfc, V2_ONLY_FLAG )); 
189     MSGF(gfc, "SPACE_V1_FLAG : %d\n", test_tag_spec_flags(gfc, SPACE_V1_FLAG)); 
190     MSGF(gfc, "PAD_V2_FLAG   : %d\n", test_tag_spec_flags(gfc, PAD_V2_FLAG  )); 
191 }
192 #endif
193
194
195
196 static int
197 id3v2_add_ucs2(lame_internal_flags * gfc, uint32_t frame_id, char const *lang,
198                unsigned short const *desc, unsigned short const *text);
199 static int
200 id3v2_add_latin1(lame_internal_flags * gfc, uint32_t frame_id, char const *lang, char const *desc,
201                  char const *text);
202
203 static void
204 copyV1ToV2(lame_internal_flags * gfc, int frame_id, char const *s)
205 {
206     unsigned int flags = gfc->tag_spec.flags;
207     id3v2_add_latin1(gfc, frame_id, 0, 0, s);
208     gfc->tag_spec.flags = flags;
209 #if 0
210     debug_tag_spec_flags(gfc, "copyV1ToV2");
211 #endif
212 }
213
214
215 static void
216 id3v2AddLameVersion(lame_internal_flags * gfc)
217 {
218     char    buffer[1024];
219     const char *b = get_lame_os_bitness();
220     const char *v = get_lame_version();
221     const char *u = get_lame_url();
222     const size_t lenb = strlen(b);
223
224     if (lenb > 0) {
225         sprintf(buffer, "LAME %s version %s (%s)", b, v, u);
226     }
227     else {
228         sprintf(buffer, "LAME version %s (%s)", v, u);
229     }
230     copyV1ToV2(gfc, ID_ENCODER, buffer);
231 }
232
233 static void
234 id3v2AddAudioDuration(lame_internal_flags * gfc, double ms)
235 {
236     SessionConfig_t const *const cfg = &gfc->cfg;
237     char    buffer[1024];
238     double const max_ulong = MAX_U_32_NUM;
239     unsigned long playlength_ms;
240
241     ms *= 1000;
242     ms /= cfg->samplerate_in;
243     if (ms > max_ulong) {
244         playlength_ms = max_ulong;
245     }
246     else if (ms < 0) {
247         playlength_ms = 0;
248     }
249     else {
250         playlength_ms = ms;
251     }
252     sprintf(buffer, "%lu", playlength_ms);
253     copyV1ToV2(gfc, ID_PLAYLENGTH, buffer);
254 }
255
256 void
257 id3tag_genre_list(void (*handler) (int, const char *, void *), void *cookie)
258 {
259     if (handler) {
260         int     i;
261         for (i = 0; i < GENRE_NAME_COUNT; ++i) {
262             if (i < GENRE_ALPHA_COUNT) {
263                 int     j = genre_alpha_map[i];
264                 handler(j, genre_names[j], cookie);
265             }
266         }
267     }
268 }
269
270 #define GENRE_NUM_UNKNOWN 255
271
272
273
274 void
275 id3tag_init(lame_global_flags * gfp)
276 {
277     lame_internal_flags *gfc = gfp->internal_flags;
278     free_id3tag(gfc);
279     memset(&gfc->tag_spec, 0, sizeof gfc->tag_spec);
280     gfc->tag_spec.genre_id3v1 = GENRE_NUM_UNKNOWN;
281     gfc->tag_spec.padding_size = 128;
282     id3v2AddLameVersion(gfc);
283 }
284
285
286
287 void
288 id3tag_add_v2(lame_global_flags * gfp)
289 {
290     lame_internal_flags *gfc = gfp->internal_flags;
291     gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
292     gfc->tag_spec.flags |= ADD_V2_FLAG;
293 }
294
295 void
296 id3tag_v1_only(lame_global_flags * gfp)
297 {
298     lame_internal_flags *gfc = gfp->internal_flags;
299     gfc->tag_spec.flags &= ~(ADD_V2_FLAG | V2_ONLY_FLAG);
300     gfc->tag_spec.flags |= V1_ONLY_FLAG;
301 }
302
303 void
304 id3tag_v2_only(lame_global_flags * gfp)
305 {
306     lame_internal_flags *gfc = gfp->internal_flags;
307     gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
308     gfc->tag_spec.flags |= V2_ONLY_FLAG;
309 }
310
311 void
312 id3tag_space_v1(lame_global_flags * gfp)
313 {
314     lame_internal_flags *gfc = gfp->internal_flags;
315     gfc->tag_spec.flags &= ~V2_ONLY_FLAG;
316     gfc->tag_spec.flags |= SPACE_V1_FLAG;
317 }
318
319 void
320 id3tag_pad_v2(lame_global_flags * gfp)
321 {
322     id3tag_set_pad(gfp, 128);
323 }
324
325 void
326 id3tag_set_pad(lame_global_flags * gfp, size_t n)
327 {
328     lame_internal_flags *gfc = gfp->internal_flags;
329     gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
330     gfc->tag_spec.flags |= PAD_V2_FLAG;
331     gfc->tag_spec.flags |= ADD_V2_FLAG;
332     gfc->tag_spec.padding_size = n;
333 }
334
335 static int
336 hasUcs2ByteOrderMarker(unsigned short bom)
337 {
338     if (bom == 0xFFFEu || bom == 0xFEFFu) {
339         return 1;
340     }
341     return 0;
342 }
343
344
345 static unsigned short
346 swap_bytes(unsigned short w)
347 {
348     return (0xff00u & (w << 8)) | (0x00ffu & (w >> 8));
349 }
350
351
352 static unsigned short
353 toLittleEndian(unsigned short bom, unsigned short c)
354 {
355     if (bom == 0xFFFEu) {
356         return swap_bytes(c);
357     }
358     return c;
359 }
360
361 static unsigned short
362 fromLatin1Char(const unsigned short* s, unsigned short c)
363 {
364     if (s[0] == 0xFFFEu) {
365         return swap_bytes(c);
366     }
367     return c;
368 }
369
370
371 static  size_t
372 local_strdup(char **dst, const char *src)
373 {
374     if (dst == 0) {
375         return 0;
376     }
377     free(*dst);
378     *dst = 0;
379     if (src != 0) {
380         size_t  n;
381         for (n = 0; src[n] != 0; ++n) { /* calc src string length */
382         }
383         if (n > 0) {    /* string length without zero termination */
384             assert(sizeof(*src) == sizeof(**dst));
385             *dst = malloc((n + 1) * sizeof(**dst));
386             if (*dst != 0) {
387                 memcpy(*dst, src, n * sizeof(**dst));
388                 (*dst)[n] = 0;
389                 return n;
390             }
391         }
392     }
393     return 0;
394 }
395
396 static  size_t
397 local_ucs2_strdup(unsigned short **dst, unsigned short const *src)
398 {
399     if (dst == 0) {
400         return 0;
401     }
402     free(*dst);         /* free old string pointer */
403     *dst = 0;
404     if (src != 0) {
405         size_t  n;
406         for (n = 0; src[n] != 0; ++n) { /* calc src string length */
407         }
408         if (n > 0) {    /* string length without zero termination */
409             assert(sizeof(*src) >= 2);
410             assert(sizeof(*src) == sizeof(**dst));
411             *dst = malloc((n + 1) * sizeof(**dst));
412             if (*dst != 0) {
413                 memcpy(*dst, src, n * sizeof(**dst));
414                 (*dst)[n] = 0;
415                 return n;
416             }
417         }
418     }
419     return 0;
420 }
421
422
423 static  size_t
424 local_ucs2_strlen(unsigned short const *s)
425 {
426     size_t  n = 0;
427     if (s != 0) {
428         while (*s++) {
429             ++n;
430         }
431     }
432     return n;
433 }
434
435
436 static size_t
437 local_ucs2_substr(unsigned short** dst, unsigned short const* src, size_t start, size_t end)
438 {
439     size_t const len = 1 + 1 + ((start < end) ? (end - start) : 0);
440     size_t n = 0;
441     unsigned short *ptr = calloc(len, sizeof(ptr[0]));
442     *dst = ptr;
443     if (ptr == 0 || src == 0) {
444         return 0;
445     }
446     if (hasUcs2ByteOrderMarker(src[0])) {
447         ptr[n++] = src[0];
448         if (start == 0) {
449             ++start;
450         }
451     }
452     while (start < end) {
453         ptr[n++] = src[start++];
454     }
455     ptr[n] = 0;
456     return n;
457 }
458
459 static int
460 local_ucs2_pos(unsigned short const* str, unsigned short c)
461 {
462     int     i;
463     for (i = 0; str != 0 && str[i] != 0; ++i) {
464         if (str[i] == c) {
465             return i;
466         }
467     }
468     return -1;
469 }
470
471 static int
472 maybeLatin1(unsigned short const* text)
473 {
474     if (text) {
475         unsigned short bom = *text++;
476         while (*text) {
477             unsigned short c = toLittleEndian(bom, *text++);
478             if (c > 0x00fe) return 0;
479         }
480     }
481     return 1;
482 }
483
484 static int searchGenre(char const* genre);
485 static int sloppySearchGenre(char const* genre);
486
487 static int
488 lookupGenre(char const* genre)
489 {
490     char   *str;
491     int     num = strtol(genre, &str, 10);
492     /* is the input a string or a valid number? */
493     if (*str) {
494         num = searchGenre(genre);
495         if (num == GENRE_NAME_COUNT) {
496             num = sloppySearchGenre(genre);
497         }
498         if (num == GENRE_NAME_COUNT) {
499             return -2; /* no common genre text found */
500         }
501     }
502     else {
503         if ((num < 0) || (num >= GENRE_NAME_COUNT)) {
504             return -1; /* number unknown */
505         }
506     }
507     return num;
508 }
509
510 static unsigned char *
511 writeLoBytes(unsigned char *frame, unsigned short const *str, size_t n);
512
513 static char*
514 local_strdup_utf16_to_latin1(unsigned short const* utf16)
515 {
516     size_t  len = local_ucs2_strlen(utf16);
517     unsigned char* latin1 = calloc(len+1, 1);
518     writeLoBytes(latin1, utf16, len);
519     return (char*)latin1;
520 }
521
522
523 static int
524 id3tag_set_genre_utf16(lame_t gfp, unsigned short const* text)
525 {
526     lame_internal_flags* gfc = gfp->internal_flags;
527     int   ret;
528     if (text == 0) {
529         return -3;
530     }
531     if (!hasUcs2ByteOrderMarker(text[0])) {
532         return -3;
533     }
534     if (maybeLatin1(text)) {
535         char*   latin1 = local_strdup_utf16_to_latin1(text);
536         int     num = lookupGenre(latin1);
537         free(latin1);
538         if (num == -1) return -1; /* number out of range */
539         if (num >= 0) {           /* common genre found  */
540             gfc->tag_spec.flags |= CHANGED_FLAG;
541             gfc->tag_spec.genre_id3v1 = num;
542             copyV1ToV2(gfc, ID_GENRE, genre_names[num]);
543             return 0;
544         }
545     }
546     ret = id3v2_add_ucs2(gfp->internal_flags, ID_GENRE, 0, 0, text);
547     if (ret == 0) {
548         gfc->tag_spec.flags |= CHANGED_FLAG;
549         gfc->tag_spec.genre_id3v1 = GENRE_INDEX_OTHER;
550     }
551     return ret;
552 }
553
554 /*
555 Some existing options for ID3 tag can be specified by --tv option
556 as follows.
557 --tt <value>, --tv TIT2=value
558 --ta <value>, --tv TPE1=value
559 --tl <value>, --tv TALB=value
560 --ty <value>, --tv TYER=value
561 --tn <value>, --tv TRCK=value
562 --tg <value>, --tv TCON=value
563 (although some are not exactly same)*/
564
565 int
566 id3tag_set_albumart(lame_global_flags * gfp, const char *image, size_t size)
567 {
568     int     mimetype = 0;
569     unsigned char const *data = (unsigned char const *) image;
570     lame_internal_flags *gfc = gfp->internal_flags;
571
572     /* determine MIME type from the actual image data */
573     if (2 < size && data[0] == 0xFF && data[1] == 0xD8) {
574         mimetype = MIMETYPE_JPEG;
575     }
576     else if (4 < size && data[0] == 0x89 && strncmp((const char *) &data[1], "PNG", 3) == 0) {
577         mimetype = MIMETYPE_PNG;
578     }
579     else if (4 < size && strncmp((const char *) data, "GIF8", 4) == 0) {
580         mimetype = MIMETYPE_GIF;
581     }
582     else {
583         return -1;
584     }
585     if (gfc->tag_spec.albumart != 0) {
586         free(gfc->tag_spec.albumart);
587         gfc->tag_spec.albumart = 0;
588         gfc->tag_spec.albumart_size = 0;
589         gfc->tag_spec.albumart_mimetype = MIMETYPE_NONE;
590     }
591     if (size < 1) {
592         return 0;
593     }
594     gfc->tag_spec.albumart = malloc(size);
595     if (gfc->tag_spec.albumart != 0) {
596         memcpy(gfc->tag_spec.albumart, image, size);
597         gfc->tag_spec.albumart_size = size;
598         gfc->tag_spec.albumart_mimetype = mimetype;
599         gfc->tag_spec.flags |= CHANGED_FLAG;
600         id3tag_add_v2(gfp);
601     }
602     return 0;
603 }
604
605 static unsigned char *
606 set_4_byte_value(unsigned char *bytes, uint32_t value)
607 {
608     int     i;
609     for (i = 3; i >= 0; --i) {
610         bytes[i] = value & 0xffUL;
611         value >>= 8;
612     }
613     return bytes + 4;
614 }
615
616 static uint32_t
617 toID3v2TagId(char const *s)
618 {
619     unsigned int i, x = 0;
620     if (s == 0) {
621         return 0;
622     }
623     for (i = 0; i < 4 && s[i] != 0; ++i) {
624         char const c = s[i];
625         unsigned int const u = 0x0ff & c;
626         x <<= 8;
627         x |= u;
628         if (c < 'A' || 'Z' < c) {
629             if (c < '0' || '9' < c) {
630                 return 0;
631             }
632         }
633     }
634     return x;
635 }
636
637 static uint32_t
638 toID3v2TagId_ucs2(unsigned short const *s)
639 {
640     unsigned int i, x = 0;
641     unsigned short bom = 0;
642     if (s == 0) {
643         return 0;
644     }
645     bom = s[0];
646     if (hasUcs2ByteOrderMarker(bom)) {
647         ++s;
648     }
649     for (i = 0; i < 4 && s[i] != 0; ++i) {
650         unsigned short const c = toLittleEndian(bom, s[i]);
651         if (c < 'A' || 'Z' < c) {
652             if (c < '0' || '9' < c) {
653                 return 0;
654             }
655         }
656         x <<= 8;
657         x |= c;
658     }
659     return x;
660 }
661
662 #if 0
663 static int
664 isNumericString(uint32_t frame_id)
665 {
666     switch (frame_id) {
667     case ID_DATE:
668     case ID_TIME:
669     case ID_TPOS:
670     case ID_TRACK:
671     case ID_YEAR:
672         return 1;
673     }
674     return 0;
675 }
676 #endif
677
678 static int
679 isMultiFrame(uint32_t frame_id)
680 {
681     switch (frame_id) {
682     case ID_TXXX:
683     case ID_WXXX:
684     case ID_COMMENT:
685     case ID_SYLT:
686     case ID_APIC:
687     case ID_GEOB:
688     case ID_PCNT:
689     case ID_AENC:
690     case ID_LINK:
691     case ID_ENCR:
692     case ID_GRID:
693     case ID_PRIV:
694         return 1;
695     }
696     return 0;
697 }
698
699 #if 0
700 static int
701 isFullTextString(int frame_id)
702 {
703     switch (frame_id) {
704     case ID_VSLT:
705     case ID_COMMENT:
706         return 1;
707     }
708     return 0;
709 }
710 #endif
711
712 static FrameDataNode *
713 findNode(id3tag_spec const *tag, uint32_t frame_id, FrameDataNode const *last)
714 {
715     FrameDataNode *node = last ? last->nxt : tag->v2_head;
716     while (node != 0) {
717         if (node->fid == frame_id) {
718             return node;
719         }
720         node = node->nxt;
721     }
722     return 0;
723 }
724
725 static void
726 appendNode(id3tag_spec * tag, FrameDataNode * node)
727 {
728     if (tag->v2_tail == 0 || tag->v2_head == 0) {
729         tag->v2_head = node;
730         tag->v2_tail = node;
731     }
732     else {
733         tag->v2_tail->nxt = node;
734         tag->v2_tail = node;
735     }
736 }
737
738 static void
739 setLang(char *dst, char const *src)
740 {
741     int     i;
742     if (src == 0 || src[0] == 0) {
743         dst[0] = 'X';
744         dst[1] = 'X';
745         dst[2] = 'X';
746     }
747     else {
748         for (i = 0; i < 3 && src && *src; ++i) {
749             dst[i] = src[i];
750         }
751         for (; i < 3; ++i) {
752             dst[i] = ' ';
753         }
754     }
755 }
756
757 static int
758 isSameLang(char const *l1, char const *l2)
759 {
760     char    d[3];
761     int     i;
762     setLang(d, l2);
763     for (i = 0; i < 3; ++i) {
764         char    a = tolower(l1[i]);
765         char    b = tolower(d[i]);
766         if (a < ' ')
767             a = ' ';
768         if (b < ' ')
769             b = ' ';
770         if (a != b) {
771             return 0;
772         }
773     }
774     return 1;
775 }
776
777 static int
778 isSameDescriptor(FrameDataNode const *node, char const *dsc)
779 {
780     size_t  i;
781     if (node->dsc.enc == 1 && node->dsc.dim > 0) {
782         return 0;
783     }
784     for (i = 0; i < node->dsc.dim; ++i) {
785         if (!dsc || node->dsc.ptr.l[i] != dsc[i]) {
786             return 0;
787         }
788     }
789     return 1;
790 }
791
792 static int
793 isSameDescriptorUcs2(FrameDataNode const *node, unsigned short const *dsc)
794 {
795     size_t  i;
796     if (node->dsc.enc != 1 && node->dsc.dim > 0) {
797         return 0;
798     }
799     for (i = 0; i < node->dsc.dim; ++i) {
800         if (!dsc || node->dsc.ptr.u[i] != dsc[i]) {
801             return 0;
802         }
803     }
804     return 1;
805 }
806
807 static int
808 id3v2_add_ucs2(lame_internal_flags * gfc, uint32_t frame_id, char const *lang,
809                unsigned short const *desc, unsigned short const *text)
810 {
811     if (gfc != 0) {
812         FrameDataNode *node = 0;
813         node = findNode(&gfc->tag_spec, frame_id, 0);
814         if (isMultiFrame(frame_id)) {
815             while (node) {
816                 if (isSameLang(node->lng, lang)) {
817                     if (isSameDescriptorUcs2(node, desc)) {
818                         break;
819                     }
820                 }
821                 node = findNode(&gfc->tag_spec, frame_id, node);
822             }
823         }
824         if (node == 0) {
825             node = calloc(1, sizeof(FrameDataNode));
826             if (node == 0) {
827                 return -254; /* memory problem */
828             }
829             appendNode(&gfc->tag_spec, node);
830         }
831         node->fid = frame_id;
832         setLang(node->lng, lang);
833         node->dsc.dim = local_ucs2_strdup(&node->dsc.ptr.u, desc);
834         node->dsc.enc = 1;
835         node->txt.dim = local_ucs2_strdup(&node->txt.ptr.u, text);
836         node->txt.enc = 1;
837         gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
838     }
839     return 0;
840 }
841
842 static int
843 id3v2_add_latin1(lame_internal_flags * gfc, uint32_t frame_id, char const *lang, char const *desc,
844                  char const *text)
845 {
846     if (gfc != 0) {
847         FrameDataNode *node = 0;
848         node = findNode(&gfc->tag_spec, frame_id, 0);
849         if (isMultiFrame(frame_id)) {
850             while (node) {
851                 if (isSameLang(node->lng, lang)) {
852                     if (isSameDescriptor(node, desc)) {
853                         break;
854                     }
855                 }
856                 node = findNode(&gfc->tag_spec, frame_id, node);
857             }
858         }
859         if (node == 0) {
860             node = calloc(1, sizeof(FrameDataNode));
861             if (node == 0) {
862                 return -254; /* memory problem */
863             }
864             appendNode(&gfc->tag_spec, node);
865         }
866         node->fid = frame_id;
867         setLang(node->lng, lang);
868         node->dsc.dim = local_strdup(&node->dsc.ptr.l, desc);
869         node->dsc.enc = 0;
870         node->txt.dim = local_strdup(&node->txt.ptr.l, text);
871         node->txt.enc = 0;
872         gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
873     }
874     return 0;
875 }
876
877
878 static int
879 id3tag_set_userinfo_latin1(lame_internal_flags* gfc, uint32_t id, char const *fieldvalue)
880 {
881     int     rc;
882     char* dsc = 0, *val;
883     local_strdup(&dsc, fieldvalue);
884     val = dsc;
885     while (*val) {
886         if (*val == '=') {
887             *val++ = 0;
888             break;
889         }
890         ++val;
891     }
892     rc = id3v2_add_latin1(gfc, id, "XXX", dsc, val);
893     free(dsc);
894     return rc;
895 }
896
897 static int
898 id3tag_set_userinfo_ucs2(lame_internal_flags* gfc, uint32_t id, unsigned short const *fieldvalue)
899 {
900     int     a, b, rc = -7;
901     unsigned short const separator = fromLatin1Char(fieldvalue,'=');
902     b = local_ucs2_strlen(fieldvalue);
903     a = local_ucs2_pos(fieldvalue, separator);
904     if (a >= 0 && a <= b) { 
905         unsigned short* dsc = 0, *val = 0;
906         local_ucs2_substr(&dsc, fieldvalue, 0, a);
907         local_ucs2_substr(&val, fieldvalue, a+1, b);
908         rc = id3v2_add_ucs2(gfc, id, "XXX", dsc, val);
909         free(dsc);
910         free(val);
911     }
912     return rc;
913 }
914
915 int
916 id3tag_set_textinfo_utf16(lame_global_flags * gfp, char const *id, unsigned short const *text)
917 {
918     uint32_t const frame_id = toID3v2TagId(id);
919     if (frame_id == 0) {
920         return -1;
921     }
922     if (isFrameIdMatching(frame_id, FRAME_ID('T', 0, 0, 0))
923       ||isFrameIdMatching(frame_id, FRAME_ID('W', 0, 0, 0))) {
924 #if 0
925         if (isNumericString(frame_id)) {
926             return -2;  /* must be Latin-1 encoded */
927         }
928 #endif
929         if (text == 0) {
930             return 0;
931         }
932         if (!hasUcs2ByteOrderMarker(text[0])) {
933             return -3;  /* BOM missing */
934         }
935         if (gfp != 0) {
936             if (frame_id == ID_TXXX || frame_id == ID_WXXX) {
937                 return id3tag_set_userinfo_ucs2(gfp->internal_flags, frame_id, text);
938             }
939             if (frame_id == ID_GENRE) {
940                 return id3tag_set_genre_utf16(gfp, text);
941             }
942             return id3v2_add_ucs2(gfp->internal_flags, frame_id, 0, 0, text);
943         }
944     }
945     return -255;        /* not supported by now */
946 }
947
948 extern int
949 id3tag_set_textinfo_ucs2(lame_global_flags * gfp, char const *id, unsigned short const *text);
950
951 int
952 id3tag_set_textinfo_ucs2(lame_global_flags * gfp, char const *id, unsigned short const *text)
953 {
954     return id3tag_set_textinfo_utf16(gfp, id, text);
955 }
956
957 int
958 id3tag_set_textinfo_latin1(lame_global_flags * gfp, char const *id, char const *text)
959 {
960     uint32_t const frame_id = toID3v2TagId(id);
961     if (frame_id == 0) {
962         return -1;
963     }
964     if (isFrameIdMatching(frame_id, FRAME_ID('T', 0, 0, 0))
965       ||isFrameIdMatching(frame_id, FRAME_ID('W', 0, 0, 0))) {
966         if (text == 0) {
967             return 0;
968         }
969         if (gfp != 0) {
970             if (frame_id == ID_TXXX || frame_id == ID_WXXX) {
971                 return id3tag_set_userinfo_latin1(gfp->internal_flags, frame_id, text);
972             }
973             return id3v2_add_latin1(gfp->internal_flags, frame_id, 0, 0, text);
974         }
975     }
976     return -255;        /* not supported by now */
977 }
978
979
980 int
981 id3tag_set_comment_latin1(lame_global_flags * gfp, char const *lang, char const *desc,
982                           char const *text)
983 {
984     if (gfp != 0) {
985         return id3v2_add_latin1(gfp->internal_flags, ID_COMMENT, lang, desc, text);
986     }
987     return -255;
988 }
989
990
991 int
992 id3tag_set_comment_utf16(lame_global_flags * gfp, char const *lang, unsigned short const *desc,
993                          unsigned short const *text)
994 {
995     if (gfp != 0) {
996         return id3v2_add_ucs2(gfp->internal_flags, ID_COMMENT, lang, desc, text);
997     }
998     return -255;
999 }
1000
1001 extern int
1002 id3tag_set_comment_ucs2(lame_global_flags * gfp, char const *lang, unsigned short const *desc,
1003                         unsigned short const *text);
1004
1005
1006 int
1007 id3tag_set_comment_ucs2(lame_global_flags * gfp, char const *lang, unsigned short const *desc,
1008                         unsigned short const *text)
1009 {
1010     return id3tag_set_comment_utf16(gfp, lang, desc, text);
1011 }
1012
1013
1014 void
1015 id3tag_set_title(lame_global_flags * gfp, const char *title)
1016 {
1017     lame_internal_flags *gfc = gfp->internal_flags;
1018     if (title && *title) {
1019         local_strdup(&gfc->tag_spec.title, title);
1020         gfc->tag_spec.flags |= CHANGED_FLAG;
1021         copyV1ToV2(gfc, ID_TITLE, title);
1022     }
1023 }
1024
1025 void
1026 id3tag_set_artist(lame_global_flags * gfp, const char *artist)
1027 {
1028     lame_internal_flags *gfc = gfp->internal_flags;
1029     if (artist && *artist) {
1030         local_strdup(&gfc->tag_spec.artist, artist);
1031         gfc->tag_spec.flags |= CHANGED_FLAG;
1032         copyV1ToV2(gfc, ID_ARTIST, artist);
1033     }
1034 }
1035
1036 void
1037 id3tag_set_album(lame_global_flags * gfp, const char *album)
1038 {
1039     lame_internal_flags *gfc = gfp->internal_flags;
1040     if (album && *album) {
1041         local_strdup(&gfc->tag_spec.album, album);
1042         gfc->tag_spec.flags |= CHANGED_FLAG;
1043         copyV1ToV2(gfc, ID_ALBUM, album);
1044     }
1045 }
1046
1047 void
1048 id3tag_set_year(lame_global_flags * gfp, const char *year)
1049 {
1050     lame_internal_flags *gfc = gfp->internal_flags;
1051     if (year && *year) {
1052         int     num = atoi(year);
1053         if (num < 0) {
1054             num = 0;
1055         }
1056         /* limit a year to 4 digits so it fits in a version 1 tag */
1057         if (num > 9999) {
1058             num = 9999;
1059         }
1060         if (num) {
1061             gfc->tag_spec.year = num;
1062             gfc->tag_spec.flags |= CHANGED_FLAG;
1063         }
1064         copyV1ToV2(gfc, ID_YEAR, year);
1065     }
1066 }
1067
1068 void
1069 id3tag_set_comment(lame_global_flags * gfp, const char *comment)
1070 {
1071     lame_internal_flags *gfc = gfp->internal_flags;
1072     if (comment && *comment) {
1073         local_strdup(&gfc->tag_spec.comment, comment);
1074         gfc->tag_spec.flags |= CHANGED_FLAG;
1075         {
1076             uint32_t const flags = gfc->tag_spec.flags;
1077             id3v2_add_latin1(gfc, ID_COMMENT, "XXX", "", comment);
1078             gfc->tag_spec.flags = flags;
1079         }
1080     }
1081 }
1082
1083 int
1084 id3tag_set_track(lame_global_flags * gfp, const char *track)
1085 {
1086     char const *trackcount;
1087     lame_internal_flags *gfc = gfp->internal_flags;
1088     int     ret = 0;
1089
1090     if (track && *track) {
1091         int     num = atoi(track);
1092         /* check for valid ID3v1 track number range */
1093         if (num < 1 || num > 255) {
1094             num = 0;
1095             ret = -1;   /* track number out of ID3v1 range, ignored for ID3v1 */
1096             gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
1097         }
1098         if (num) {
1099             gfc->tag_spec.track_id3v1 = num;
1100             gfc->tag_spec.flags |= CHANGED_FLAG;
1101         }
1102         /* Look for the total track count after a "/", same restrictions */
1103         trackcount = strchr(track, '/');
1104         if (trackcount && *trackcount) {
1105             gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
1106         }
1107         copyV1ToV2(gfc, ID_TRACK, track);
1108     }
1109     return ret;
1110 }
1111
1112 /* would use real "strcasecmp" but it isn't portable */
1113 static int
1114 local_strcasecmp(const char *s1, const char *s2)
1115 {
1116     unsigned char c1;
1117     unsigned char c2;
1118     do {
1119         c1 = tolower(*s1);
1120         c2 = tolower(*s2);
1121         if (!c1) {
1122             break;
1123         }
1124         ++s1;
1125         ++s2;
1126     } while (c1 == c2);
1127     return c1 - c2;
1128 }
1129
1130
1131 static 
1132 const char* nextUpperAlpha(const char* p, char x)
1133 {
1134     char c;
1135     for(c = toupper(*p); *p != 0; c = toupper(*++p)) {
1136         if ('A' <= c && c <= 'Z') {
1137             if (c != x) {
1138                 return p;
1139             }
1140         }
1141     }
1142     return p;
1143 }
1144
1145
1146 static int
1147 sloppyCompared(const char* p, const char* q)
1148 {
1149     char cp, cq;
1150     p = nextUpperAlpha(p, 0);
1151     q = nextUpperAlpha(q, 0);
1152     cp = toupper(*p);
1153     cq = toupper(*q);
1154     while (cp == cq) {
1155         if (cp == 0) {
1156             return 1;
1157         }
1158         if (p[1] == '.') { /* some abbrevation */
1159             while (*q && *q++ != ' ') {
1160             }
1161         }
1162         p = nextUpperAlpha(p, cp);
1163         q = nextUpperAlpha(q, cq);
1164         cp = toupper(*p);
1165         cq = toupper(*q);
1166     }
1167     return 0;
1168 }
1169
1170
1171 static int 
1172 sloppySearchGenre(const char *genre)
1173 {
1174     int i;
1175     for (i = 0; i < GENRE_NAME_COUNT; ++i) {
1176         if (sloppyCompared(genre, genre_names[i])) {
1177             return i;
1178         }
1179     }
1180     return GENRE_NAME_COUNT;
1181 }
1182
1183
1184 static int
1185 searchGenre(const char* genre)
1186 {
1187     int i;
1188     for (i = 0; i < GENRE_NAME_COUNT; ++i) {
1189         if (!local_strcasecmp(genre, genre_names[i])) {
1190             return i;
1191         }
1192     }
1193     return GENRE_NAME_COUNT;
1194 }
1195
1196
1197 int
1198 id3tag_set_genre(lame_global_flags * gfp, const char *genre)
1199 {
1200     lame_internal_flags *gfc = gfp->internal_flags;
1201     int     ret = 0;
1202     if (genre && *genre) {
1203         int const num = lookupGenre(genre);
1204         if (num == -1) return num;
1205         gfc->tag_spec.flags |= CHANGED_FLAG;
1206         if (num >= 0) {
1207             gfc->tag_spec.genre_id3v1 = num;
1208             genre = genre_names[num];
1209         }
1210         else {
1211             gfc->tag_spec.genre_id3v1 = GENRE_INDEX_OTHER;
1212             gfc->tag_spec.flags |= ADD_V2_FLAG;
1213         }
1214         copyV1ToV2(gfc, ID_GENRE, genre);
1215     }
1216     return ret;
1217 }
1218
1219
1220 static unsigned char *
1221 set_frame_custom(unsigned char *frame, const char *fieldvalue)
1222 {
1223     if (fieldvalue && *fieldvalue) {
1224         const char *value = fieldvalue + 5;
1225         size_t  length = strlen(value);
1226         *frame++ = *fieldvalue++;
1227         *frame++ = *fieldvalue++;
1228         *frame++ = *fieldvalue++;
1229         *frame++ = *fieldvalue++;
1230         frame = set_4_byte_value(frame, (unsigned long) (strlen(value) + 1));
1231         /* clear 2-byte header flags */
1232         *frame++ = 0;
1233         *frame++ = 0;
1234         /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
1235         *frame++ = 0;
1236         while (length--) {
1237             *frame++ = *value++;
1238         }
1239     }
1240     return frame;
1241 }
1242
1243 static  size_t
1244 sizeOfNode(FrameDataNode const *node)
1245 {
1246     size_t  n = 0;
1247     if (node) {
1248         n = 10;         /* header size */
1249         n += 1;         /* text encoding flag */
1250         switch (node->txt.enc) {
1251         default:
1252         case 0:
1253             if (node->dsc.dim > 0) {
1254                 n += node->dsc.dim + 1;
1255             }
1256             n += node->txt.dim;
1257             break;
1258         case 1:
1259             if (node->dsc.dim > 0) {
1260                 n += (node->dsc.dim+1) * 2;
1261             }
1262             n += node->txt.dim * 2;
1263             break;
1264         }
1265     }
1266     return n;
1267 }
1268
1269 static  size_t
1270 sizeOfCommentNode(FrameDataNode const *node)
1271 {
1272     size_t  n = 0;
1273     if (node) {
1274         n = 10;         /* header size */
1275         n += 1;         /* text encoding flag */
1276         n += 3;         /* language */
1277         switch (node->dsc.enc) {
1278         default:
1279         case 0:
1280             n += 1 + node->dsc.dim;
1281             break;
1282         case 1:
1283             n += 2 + node->dsc.dim * 2;
1284             break;
1285         }
1286         switch (node->txt.enc) {
1287         default:
1288         case 0:
1289             n += node->txt.dim;
1290             break;
1291         case 1:
1292             n += node->txt.dim * 2;
1293             break;
1294         }
1295     }
1296     return n;
1297 }
1298
1299 static size_t
1300 sizeOfWxxxNode(FrameDataNode const *node)
1301 {
1302     size_t  n = 0;
1303     if (node) {
1304         n = 10;         /* header size */
1305         if (node->dsc.dim > 0) {
1306             n += 1;         /* text encoding flag */
1307             switch (node->dsc.enc) {
1308             default:
1309             case 0:
1310                 n += 1 + node->dsc.dim;
1311                 break;
1312             case 1:
1313                 n += 2 + node->dsc.dim * 2;
1314                 break;
1315             }
1316         }
1317         switch (node->txt.enc) {
1318         default:
1319         case 0:
1320             n += node->txt.dim;
1321             break;
1322         case 1:
1323             n += node->txt.dim - 1; /* UCS2 -> Latin1, skip BOM */
1324             break;
1325         }
1326     }
1327     return n;
1328 }
1329
1330 static unsigned char *
1331 writeChars(unsigned char *frame, char const *str, size_t n)
1332 {
1333     while (n--) {
1334         *frame++ = *str++;
1335     }
1336     return frame;
1337 }
1338
1339 static unsigned char *
1340 writeUcs2s(unsigned char *frame, unsigned short const *str, size_t n)
1341 {
1342     if (n > 0) {
1343         unsigned short const bom = *str;
1344         while (n--) {
1345             unsigned short const c = toLittleEndian(bom, *str++);
1346             *frame++ = 0x00ffu & c;
1347             *frame++ = 0x00ffu & (c >> 8);
1348         }
1349     }
1350     return frame;
1351 }
1352
1353 static unsigned char *
1354 writeLoBytes(unsigned char *frame, unsigned short const *str, size_t n)
1355 {
1356     if (n > 0) {
1357         unsigned short const bom = *str;
1358         if (hasUcs2ByteOrderMarker(bom)) {
1359             str++; n--; /* skip BOM */
1360         }
1361         while (n--) {
1362             unsigned short const c = toLittleEndian(bom, *str++);
1363             if (c < 0x0020u || 0x00ffu < c) {
1364                 *frame++ = 0x0020; /* blank */
1365             }
1366             else {
1367                 *frame++ = c;
1368             }
1369         }
1370     }
1371     return frame;
1372 }
1373
1374 static unsigned char *
1375 set_frame_comment(unsigned char *frame, FrameDataNode const *node)
1376 {
1377     size_t const n = sizeOfCommentNode(node);
1378     if (n > 10) {
1379         frame = set_4_byte_value(frame, node->fid);
1380         frame = set_4_byte_value(frame, (uint32_t) (n - 10));
1381         /* clear 2-byte header flags */
1382         *frame++ = 0;
1383         *frame++ = 0;
1384         /* encoding descriptor byte */
1385         *frame++ = node->txt.enc == 1 ? 1 : 0;
1386         /* 3 bytes language */
1387         *frame++ = node->lng[0];
1388         *frame++ = node->lng[1];
1389         *frame++ = node->lng[2];
1390         /* descriptor with zero byte(s) separator */
1391         if (node->dsc.enc != 1) {
1392             frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
1393             *frame++ = 0;
1394         }
1395         else {
1396             frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
1397             *frame++ = 0;
1398             *frame++ = 0;
1399         }
1400         /* comment full text */
1401         if (node->txt.enc != 1) {
1402             frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
1403         }
1404         else {
1405             frame = writeUcs2s(frame, node->txt.ptr.u, node->txt.dim);
1406         }
1407     }
1408     return frame;
1409 }
1410
1411 static unsigned char *
1412 set_frame_custom2(unsigned char *frame, FrameDataNode const *node)
1413 {
1414     size_t const n = sizeOfNode(node);
1415     if (n > 10) {
1416         frame = set_4_byte_value(frame, node->fid);
1417         frame = set_4_byte_value(frame, (unsigned long) (n - 10));
1418         /* clear 2-byte header flags */
1419         *frame++ = 0;
1420         *frame++ = 0;
1421         /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
1422         *frame++ = node->txt.enc == 1 ? 1 : 0;
1423         if (node->dsc.dim > 0) {
1424             if (node->dsc.enc != 1) {
1425                 frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
1426                 *frame++ = 0;
1427             }
1428             else {
1429                 frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
1430                 *frame++ = 0;
1431                 *frame++ = 0;
1432             }
1433         }
1434         if (node->txt.enc != 1) {
1435             frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
1436         }
1437         else {
1438             frame = writeUcs2s(frame, node->txt.ptr.u, node->txt.dim);
1439         }
1440     }
1441     return frame;
1442 }
1443
1444 static unsigned char *
1445 set_frame_wxxx(unsigned char *frame, FrameDataNode const *node)
1446 {
1447     size_t const n = sizeOfWxxxNode(node);
1448     if (n > 10) {
1449         frame = set_4_byte_value(frame, node->fid);
1450         frame = set_4_byte_value(frame, (unsigned long) (n - 10));
1451         /* clear 2-byte header flags */
1452         *frame++ = 0;
1453         *frame++ = 0;
1454         if (node->dsc.dim > 0) {
1455             /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
1456             *frame++ = node->txt.enc == 1 ? 1 : 0;
1457             if (node->dsc.enc != 1) {
1458                 frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
1459                 *frame++ = 0;
1460             }
1461             else {
1462                 frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
1463                 *frame++ = 0;
1464                 *frame++ = 0;
1465             }
1466         }
1467         if (node->txt.enc != 1) {
1468             frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
1469         }
1470         else {
1471             frame = writeLoBytes(frame, node->txt.ptr.u, node->txt.dim);
1472         }
1473     }
1474     return frame;
1475 }
1476
1477 static unsigned char *
1478 set_frame_apic(unsigned char *frame, const char *mimetype, const unsigned char *data, size_t size)
1479 {
1480     /* ID3v2.3 standard APIC frame:
1481      *     <Header for 'Attached picture', ID: "APIC">
1482      *     Text encoding    $xx
1483      *     MIME type        <text string> $00
1484      *     Picture type     $xx
1485      *     Description      <text string according to encoding> $00 (00)
1486      *     Picture data     <binary data>
1487      */
1488     if (mimetype && data && size) {
1489         frame = set_4_byte_value(frame, FRAME_ID('A', 'P', 'I', 'C'));
1490         frame = set_4_byte_value(frame, (unsigned long) (4 + strlen(mimetype) + size));
1491         /* clear 2-byte header flags */
1492         *frame++ = 0;
1493         *frame++ = 0;
1494         /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
1495         *frame++ = 0;
1496         /* copy mime_type */
1497         while (*mimetype) {
1498             *frame++ = *mimetype++;
1499         }
1500         *frame++ = 0;
1501         /* set picture type to 0 */
1502         *frame++ = 0;
1503         /* empty description field */
1504         *frame++ = 0;
1505         /* copy the image data */
1506         while (size--) {
1507             *frame++ = *data++;
1508         }
1509     }
1510     return frame;
1511 }
1512
1513 int
1514 id3tag_set_fieldvalue(lame_global_flags * gfp, const char *fieldvalue)
1515 {
1516     lame_internal_flags *gfc = gfp->internal_flags;
1517     if (fieldvalue && *fieldvalue) {
1518         uint32_t const frame_id = toID3v2TagId(fieldvalue);
1519         char  **p = NULL;
1520         if (strlen(fieldvalue) < 5 || fieldvalue[4] != '=') {
1521             return -1;
1522         }
1523         if (frame_id != 0) {
1524             if (id3tag_set_textinfo_latin1(gfp, fieldvalue, &fieldvalue[5])) {
1525                 p = (char **) realloc(gfc->tag_spec.values,
1526                                       sizeof(char *) * (gfc->tag_spec.num_values + 1));
1527                 if (!p) {
1528                     return -1;
1529                 }
1530                 gfc->tag_spec.values = (char **) p;
1531                 local_strdup(&gfc->tag_spec.values[gfc->tag_spec.num_values++], fieldvalue);
1532             }
1533         }
1534         gfc->tag_spec.flags |= CHANGED_FLAG;
1535     }
1536     id3tag_add_v2(gfp);
1537     return 0;
1538 }
1539
1540 int
1541 id3tag_set_fieldvalue_utf16(lame_global_flags * gfp, const unsigned short *fieldvalue)
1542 {
1543     if (fieldvalue && *fieldvalue) {
1544         size_t dx = hasUcs2ByteOrderMarker(fieldvalue[0]);
1545         unsigned short const separator = fromLatin1Char(fieldvalue, '=');
1546         char fid[5] = {0,0,0,0,0};
1547         uint32_t const frame_id = toID3v2TagId_ucs2(fieldvalue);
1548         if (local_ucs2_strlen(fieldvalue) < (5+dx) || fieldvalue[4+dx] != separator) {
1549             return -1;
1550         }
1551         fid[0] = (frame_id >> 24) & 0x0ff;
1552         fid[1] = (frame_id >> 16) & 0x0ff;
1553         fid[2] = (frame_id >> 8) & 0x0ff;
1554         fid[3] = frame_id & 0x0ff;
1555         if (frame_id != 0) {
1556             unsigned short* txt = 0;
1557             int     rc;
1558             local_ucs2_substr(&txt, fieldvalue, dx+5, local_ucs2_strlen(fieldvalue));
1559             rc = id3tag_set_textinfo_ucs2(gfp, fid, txt);
1560             free(txt);
1561             return rc;
1562         }
1563     }
1564     return -1;
1565 }
1566
1567 extern int
1568 id3tag_set_fieldvalue_ucs2(lame_global_flags * gfp, const unsigned short *fieldvalue);
1569
1570 int
1571 id3tag_set_fieldvalue_ucs2(lame_global_flags * gfp, const unsigned short *fieldvalue)
1572 {
1573     return id3tag_set_fieldvalue_utf16(gfp, fieldvalue);
1574 }
1575
1576 size_t
1577 lame_get_id3v2_tag(lame_global_flags * gfp, unsigned char *buffer, size_t size)
1578 {
1579     lame_internal_flags *gfc;
1580     if (gfp == 0) {
1581         return 0;
1582     }
1583     gfc = gfp->internal_flags;
1584     if (gfc == 0) {
1585         return 0;
1586     }
1587     if (test_tag_spec_flags(gfc, V1_ONLY_FLAG)) {
1588         return 0;
1589     }
1590 #if 0
1591     debug_tag_spec_flags(gfc, "lame_get_id3v2_tag");
1592 #endif
1593     {
1594         int usev2 = test_tag_spec_flags(gfc, ADD_V2_FLAG | V2_ONLY_FLAG);
1595         /* calculate length of four fields which may not fit in verion 1 tag */
1596         size_t  title_length = gfc->tag_spec.title ? strlen(gfc->tag_spec.title) : 0;
1597         size_t  artist_length = gfc->tag_spec.artist ? strlen(gfc->tag_spec.artist) : 0;
1598         size_t  album_length = gfc->tag_spec.album ? strlen(gfc->tag_spec.album) : 0;
1599         size_t  comment_length = gfc->tag_spec.comment ? strlen(gfc->tag_spec.comment) : 0;
1600         /* write tag if explicitly requested or if fields overflow */
1601         if ((title_length > 30)
1602             || (artist_length > 30)
1603             || (album_length > 30)
1604             || (comment_length > 30)
1605             || (gfc->tag_spec.track_id3v1 && (comment_length > 28))) {
1606             usev2 = 1;
1607         }
1608         if (usev2) {
1609             size_t  tag_size;
1610             unsigned char *p;
1611             size_t  adjusted_tag_size;
1612             unsigned int i;
1613             const char *albumart_mime = NULL;
1614             static const char *mime_jpeg = "image/jpeg";
1615             static const char *mime_png = "image/png";
1616             static const char *mime_gif = "image/gif";
1617
1618             if (gfp->num_samples != MAX_U_32_NUM) {
1619                 id3v2AddAudioDuration(gfc, gfp->num_samples);
1620             }
1621
1622             /* calulate size of tag starting with 10-byte tag header */
1623             tag_size = 10;
1624             for (i = 0; i < gfc->tag_spec.num_values; ++i) {
1625                 tag_size += 6 + strlen(gfc->tag_spec.values[i]);
1626             }
1627             if (gfc->tag_spec.albumart && gfc->tag_spec.albumart_size) {
1628                 switch (gfc->tag_spec.albumart_mimetype) {
1629                 case MIMETYPE_JPEG:
1630                     albumart_mime = mime_jpeg;
1631                     break;
1632                 case MIMETYPE_PNG:
1633                     albumart_mime = mime_png;
1634                     break;
1635                 case MIMETYPE_GIF:
1636                     albumart_mime = mime_gif;
1637                     break;
1638                 }
1639                 if (albumart_mime) {
1640                     tag_size += 10 + 4 + strlen(albumart_mime) + gfc->tag_spec.albumart_size;
1641                 }
1642             }
1643             {
1644                 id3tag_spec *tag = &gfc->tag_spec;
1645                 if (tag->v2_head != 0) {
1646                     FrameDataNode *node;
1647                     for (node = tag->v2_head; node != 0; node = node->nxt) {
1648                         if (node->fid == ID_COMMENT) {
1649                             tag_size += sizeOfCommentNode(node);
1650                         }
1651                         else if (isFrameIdMatching(node->fid, FRAME_ID('W',0,0,0))) {
1652                             tag_size += sizeOfWxxxNode(node);
1653                         }
1654                         else {
1655                             tag_size += sizeOfNode(node);
1656                         }
1657                     }
1658                 }
1659             }
1660             if (test_tag_spec_flags(gfc, PAD_V2_FLAG)) {
1661                 /* add some bytes of padding */
1662                 tag_size += gfc->tag_spec.padding_size;
1663             }
1664             if (size < tag_size) {
1665                 return tag_size;
1666             }
1667             if (buffer == 0) {
1668                 return 0;
1669             }
1670             p = buffer;
1671             /* set tag header starting with file identifier */
1672             *p++ = 'I';
1673             *p++ = 'D';
1674             *p++ = '3';
1675             /* set version number word */
1676             *p++ = 3;
1677             *p++ = 0;
1678             /* clear flags byte */
1679             *p++ = 0;
1680             /* calculate and set tag size = total size - header size */
1681             adjusted_tag_size = tag_size - 10;
1682             /* encode adjusted size into four bytes where most significant 
1683              * bit is clear in each byte, for 28-bit total */
1684             *p++ = (unsigned char) ((adjusted_tag_size >> 21) & 0x7fu);
1685             *p++ = (unsigned char) ((adjusted_tag_size >> 14) & 0x7fu);
1686             *p++ = (unsigned char) ((adjusted_tag_size >> 7) & 0x7fu);
1687             *p++ = (unsigned char) (adjusted_tag_size & 0x7fu);
1688
1689             /*
1690              * NOTE: The remainder of the tag (frames and padding, if any)
1691              * are not "unsynchronized" to prevent false MPEG audio headers
1692              * from appearing in the bitstream.  Why?  Well, most players
1693              * and utilities know how to skip the ID3 version 2 tag by now
1694              * even if they don't read its contents, and it's actually
1695              * very unlikely that such a false "sync" pattern would occur
1696              * in just the simple text frames added here.
1697              */
1698
1699             /* set each frame in tag */
1700             {
1701                 id3tag_spec *tag = &gfc->tag_spec;
1702                 if (tag->v2_head != 0) {
1703                     FrameDataNode *node;
1704                     for (node = tag->v2_head; node != 0; node = node->nxt) {
1705                         if (node->fid == ID_COMMENT) {
1706                             p = set_frame_comment(p, node);
1707                         }
1708                         else if (isFrameIdMatching(node->fid,FRAME_ID('W',0,0,0))) {
1709                             p = set_frame_wxxx(p, node);
1710                         }
1711                         else {
1712                             p = set_frame_custom2(p, node);
1713                         }
1714                     }
1715                 }
1716             }
1717             for (i = 0; i < gfc->tag_spec.num_values; ++i) {
1718                 p = set_frame_custom(p, gfc->tag_spec.values[i]);
1719             }
1720             if (albumart_mime) {
1721                 p = set_frame_apic(p, albumart_mime, gfc->tag_spec.albumart,
1722                                    gfc->tag_spec.albumart_size);
1723             }
1724             /* clear any padding bytes */
1725             memset(p, 0, tag_size - (p - buffer));
1726             return tag_size;
1727         }
1728     }
1729     return 0;
1730 }
1731
1732 int
1733 id3tag_write_v2(lame_global_flags * gfp)
1734 {
1735     lame_internal_flags *gfc = gfp->internal_flags;
1736 #if 0
1737     debug_tag_spec_flags(gfc, "write v2");
1738 #endif
1739     if (test_tag_spec_flags(gfc, V1_ONLY_FLAG)) {
1740         return 0;
1741     }
1742     if (test_tag_spec_flags(gfc, CHANGED_FLAG)) {
1743         unsigned char *tag = 0;
1744         size_t  tag_size, n;
1745
1746         n = lame_get_id3v2_tag(gfp, 0, 0);
1747         tag = malloc(n);
1748         if (tag == 0) {
1749             return -1;
1750         }
1751         tag_size = lame_get_id3v2_tag(gfp, tag, n);
1752         if (tag_size > n) {
1753             free(tag);
1754             return -1;
1755         }
1756         else {
1757             size_t  i;
1758             /* write tag directly into bitstream at current position */
1759             for (i = 0; i < tag_size; ++i) {
1760                 add_dummy_byte(gfc, tag[i], 1);
1761             }
1762         }
1763         free(tag);
1764         return (int) tag_size; /* ok, tag should not exceed 2GB */
1765     }
1766     return 0;
1767 }
1768
1769 static unsigned char *
1770 set_text_field(unsigned char *field, const char *text, size_t size, int pad)
1771 {
1772     while (size--) {
1773         if (text && *text) {
1774             *field++ = *text++;
1775         }
1776         else {
1777             *field++ = pad;
1778         }
1779     }
1780     return field;
1781 }
1782
1783 size_t
1784 lame_get_id3v1_tag(lame_global_flags * gfp, unsigned char *buffer, size_t size)
1785 {
1786     size_t const tag_size = 128;
1787     lame_internal_flags *gfc;
1788
1789     if (gfp == 0) {
1790         return 0;
1791     }
1792     if (size < tag_size) {
1793         return tag_size;
1794     }
1795     gfc = gfp->internal_flags;
1796     if (gfc == 0) {
1797         return 0;
1798     }
1799     if (buffer == 0) {
1800         return 0;
1801     }
1802     if (test_tag_spec_flags(gfc, V2_ONLY_FLAG)) {
1803         return 0;
1804     }
1805     if (test_tag_spec_flags(gfc, CHANGED_FLAG)) {
1806         unsigned char *p = buffer;
1807         int     pad = test_tag_spec_flags(gfc, SPACE_V1_FLAG) ? ' ' : 0;
1808         char    year[5];
1809
1810         /* set tag identifier */
1811         *p++ = 'T';
1812         *p++ = 'A';
1813         *p++ = 'G';
1814         /* set each field in tag */
1815         p = set_text_field(p, gfc->tag_spec.title, 30, pad);
1816         p = set_text_field(p, gfc->tag_spec.artist, 30, pad);
1817         p = set_text_field(p, gfc->tag_spec.album, 30, pad);
1818         sprintf(year, "%d", gfc->tag_spec.year);
1819         p = set_text_field(p, gfc->tag_spec.year ? year : NULL, 4, pad);
1820         /* limit comment field to 28 bytes if a track is specified */
1821         p = set_text_field(p, gfc->tag_spec.comment, gfc->tag_spec.track_id3v1 ? 28 : 30, pad);
1822         if (gfc->tag_spec.track_id3v1) {
1823             /* clear the next byte to indicate a version 1.1 tag */
1824             *p++ = 0;
1825             *p++ = gfc->tag_spec.track_id3v1;
1826         }
1827         *p++ = gfc->tag_spec.genre_id3v1;
1828         return tag_size;
1829     }
1830     return 0;
1831 }
1832
1833 int
1834 id3tag_write_v1(lame_global_flags * gfp)
1835 {
1836     lame_internal_flags *const gfc = gfp->internal_flags;
1837     size_t  i, n, m;
1838     unsigned char tag[128];
1839
1840     m = sizeof(tag);
1841     n = lame_get_id3v1_tag(gfp, tag, m);
1842     if (n > m) {
1843         return 0;
1844     }
1845     /* write tag directly into bitstream at current position */
1846     for (i = 0; i < n; ++i) {
1847         add_dummy_byte(gfc, tag[i], 1);
1848     }
1849     return (int) n;     /* ok, tag has fixed size of 128 bytes, well below 2GB */
1850 }