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