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