Blame libmp3lame/id3tag.c

Packit 47f805
/*
Packit 47f805
 * id3tag.c -- Write ID3 version 1 and 2 tags.
Packit 47f805
 *
Packit 47f805
 * Copyright (C) 2000 Don Melton
Packit 47f805
 * Copyright (C) 2011-2017 Robert Hegemann
Packit 47f805
 *
Packit 47f805
 * This library is free software; you can redistribute it and/or
Packit 47f805
 * modify it under the terms of the GNU Library General Public
Packit 47f805
 * License as published by the Free Software Foundation; either
Packit 47f805
 * version 2 of the License, or (at your option) any later version.
Packit 47f805
 *
Packit 47f805
 * This library is distributed in the hope that it will be useful,
Packit 47f805
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 47f805
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 47f805
 * Library General Public License for more details.
Packit 47f805
 *
Packit 47f805
 * You should have received a copy of the GNU Library General Public
Packit 47f805
 * License along with this library; if not, write to the Free Software
Packit 47f805
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
Packit 47f805
 */
Packit 47f805
Packit 47f805
/*
Packit 47f805
 * HISTORY: This source file is part of LAME (see http://www.mp3dev.org)
Packit 47f805
 * and was originally adapted by Conrad Sanderson <c.sanderson@me.gu.edu.au>
Packit 47f805
 * from mp3info by Ricardo Cerqueira <rmc@rccn.net> to write only ID3 version 1
Packit 47f805
 * tags.  Don Melton <don@blivet.com> COMPLETELY rewrote it to support version
Packit 47f805
 * 2 tags and be more conformant to other standards while remaining flexible.
Packit 47f805
 *
Packit 47f805
 * NOTE: See http://id3.org/ for more information about ID3 tag formats.
Packit 47f805
 */
Packit 47f805
Packit 47f805
/* $Id: id3tag.c,v 1.80 2017/08/28 15:39:51 robert Exp $ */
Packit 47f805
Packit 47f805
#ifdef HAVE_CONFIG_H
Packit 47f805
#include <config.h>
Packit 47f805
#endif
Packit 47f805
Packit 47f805
#ifdef STDC_HEADERS
Packit 47f805
# include <stddef.h>
Packit 47f805
# include <stdlib.h>
Packit 47f805
# include <string.h>
Packit 47f805
# include <ctype.h>
Packit 47f805
#else
Packit 47f805
# ifndef HAVE_STRCHR
Packit 47f805
#  define strchr index
Packit 47f805
#  define strrchr rindex
Packit 47f805
# endif
Packit 47f805
char   *strchr(), *strrchr();
Packit 47f805
# ifndef HAVE_MEMCPY
Packit 47f805
#  define memcpy(d, s, n) bcopy ((s), (d), (n))
Packit 47f805
# endif
Packit 47f805
#endif
Packit 47f805
Packit 47f805
Packit 47f805
#include "lame.h"
Packit 47f805
#include "machine.h"
Packit 47f805
#include "encoder.h"
Packit 47f805
#include "id3tag.h"
Packit 47f805
#include "lame_global_flags.h"
Packit 47f805
#include "util.h"
Packit 47f805
#include "bitstream.h"
Packit 47f805
Packit 47f805
Packit 47f805
static const char *const genre_names[] = {
Packit 47f805
    /*
Packit 47f805
     * NOTE: The spelling of these genre names is identical to those found in
Packit 47f805
     * Winamp and mp3info.
Packit 47f805
     */
Packit 47f805
    "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
Packit 47f805
    "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B",
Packit 47f805
    "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
Packit 47f805
    "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
Packit 47f805
    "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
Packit 47f805
    "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock",
Packit 47f805
    "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
Packit 47f805
    "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
Packit 47f805
    "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
Packit 47f805
    "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
Packit 47f805
    "Native US", "Cabaret", "New Wave", "Psychedelic", "Rave",
Packit 47f805
    "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz",
Packit 47f805
    "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
Packit 47f805
    "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin",
Packit 47f805
    "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
Packit 47f805
    "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
Packit 47f805
    "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech",
Packit 47f805
    "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass",
Packit 47f805
    "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
Packit 47f805
    "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
Packit 47f805
    "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall",
Packit 47f805
    "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie",
Packit 47f805
    "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta",
Packit 47f805
    "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian",
Packit 47f805
    "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
Packit 47f805
    "SynthPop"
Packit 47f805
};
Packit 47f805
Packit 47f805
#define GENRE_NAME_COUNT \
Packit 47f805
    ((int)(sizeof genre_names / sizeof (const char *const)))
Packit 47f805
Packit 47f805
static const int genre_alpha_map[] = {
Packit 47f805
    123, 34, 74, 73, 99, 20, 40, 26, 145, 90, 116, 41, 135, 85, 96, 138, 89, 0,
Packit 47f805
    107, 132, 65, 88, 104, 102, 97, 136, 61, 141, 32, 1, 112, 128, 57, 140, 2,
Packit 47f805
    139, 58, 3, 125, 50, 22, 4, 55, 127, 122, 120, 98, 52, 48, 54, 124, 25, 84,
Packit 47f805
    80, 115, 81, 119, 5, 30, 36, 59, 126, 38, 49, 91, 6, 129, 79, 137, 7, 35,
Packit 47f805
    100, 131, 19, 33, 46, 47, 8, 29, 146, 63, 86, 71, 45, 142, 9, 77, 82, 64,
Packit 47f805
    133, 10, 66, 39, 11, 103, 12, 75, 134, 13, 53, 62, 109, 117, 23, 108, 92,
Packit 47f805
    67, 93, 43, 121, 15, 68, 14, 16, 76, 87, 118, 17, 78, 143, 114, 110, 69, 21,
Packit 47f805
    111, 95, 105, 42, 37, 24, 56, 44, 101, 83, 94, 106, 147, 113, 18, 51, 130,
Packit 47f805
    144, 60, 70, 31, 72, 27, 28
Packit 47f805
};
Packit 47f805
Packit 47f805
#define GENRE_ALPHA_COUNT ((int)(sizeof genre_alpha_map / sizeof (int)))
Packit 47f805
Packit 47f805
#define GENRE_INDEX_OTHER 12
Packit 47f805
Packit 47f805
Packit 47f805
#define FRAME_ID(a, b, c, d) \
Packit 47f805
    ( ((unsigned long)(a) << 24) \
Packit 47f805
    | ((unsigned long)(b) << 16) \
Packit 47f805
    | ((unsigned long)(c) <<  8) \
Packit 47f805
    | ((unsigned long)(d) <<  0) )
Packit 47f805
Packit 47f805
typedef enum UsualStringIDs { ID_TITLE = FRAME_ID('T', 'I', 'T', '2')
Packit 47f805
        , ID_ARTIST = FRAME_ID('T', 'P', 'E', '1')
Packit 47f805
        , ID_ALBUM = FRAME_ID('T', 'A', 'L', 'B')
Packit 47f805
        , ID_GENRE = FRAME_ID('T', 'C', 'O', 'N')
Packit 47f805
        , ID_ENCODER = FRAME_ID('T', 'S', 'S', 'E')
Packit 47f805
        , ID_PLAYLENGTH = FRAME_ID('T', 'L', 'E', 'N')
Packit 47f805
        , ID_COMMENT = FRAME_ID('C', 'O', 'M', 'M') /* full text string */
Packit 47f805
} UsualStringIDs;
Packit 47f805
Packit 47f805
typedef enum NumericStringIDs { ID_DATE = FRAME_ID('T', 'D', 'A', 'T') /* "ddMM" */
Packit 47f805
        , ID_TIME = FRAME_ID('T', 'I', 'M', 'E') /* "hhmm" */
Packit 47f805
        , ID_TPOS = FRAME_ID('T', 'P', 'O', 'S') /* '0'-'9' and '/' allowed */
Packit 47f805
        , ID_TRACK = FRAME_ID('T', 'R', 'C', 'K') /* '0'-'9' and '/' allowed */
Packit 47f805
        , ID_YEAR = FRAME_ID('T', 'Y', 'E', 'R') /* "yyyy" */
Packit 47f805
} NumericStringIDs;
Packit 47f805
Packit 47f805
typedef enum MiscIDs { ID_TXXX = FRAME_ID('T', 'X', 'X', 'X')
Packit 47f805
        , ID_WXXX = FRAME_ID('W', 'X', 'X', 'X')
Packit 47f805
        , ID_SYLT = FRAME_ID('S', 'Y', 'L', 'T')
Packit 47f805
        , ID_APIC = FRAME_ID('A', 'P', 'I', 'C')
Packit 47f805
        , ID_GEOB = FRAME_ID('G', 'E', 'O', 'B')
Packit 47f805
        , ID_PCNT = FRAME_ID('P', 'C', 'N', 'T')
Packit 47f805
        , ID_AENC = FRAME_ID('A', 'E', 'N', 'C')
Packit 47f805
        , ID_LINK = FRAME_ID('L', 'I', 'N', 'K')
Packit 47f805
        , ID_ENCR = FRAME_ID('E', 'N', 'C', 'R')
Packit 47f805
        , ID_GRID = FRAME_ID('G', 'R', 'I', 'D')
Packit 47f805
        , ID_PRIV = FRAME_ID('P', 'R', 'I', 'V')
Packit 47f805
        , ID_USLT = FRAME_ID('U', 'S', 'L', 'T') /* full text string */
Packit 47f805
        , ID_USER = FRAME_ID('U', 'S', 'E', 'R') /* full text string */
Packit 47f805
        , ID_PCST = FRAME_ID('P', 'C', 'S', 'T') /* iTunes Podcast indicator, only presence important */
Packit 47f805
        , ID_WFED = FRAME_ID('W', 'F', 'E', 'D') /* iTunes Podcast URL as TEXT FRAME !!! violates standard */
Packit 47f805
} MiscIDs;
Packit 47f805
Packit 47f805
Packit 47f805
static int
Packit 47f805
frame_id_matches(int id, int mask)
Packit 47f805
{
Packit 47f805
    int     result = 0, i, window = 0xff;
Packit 47f805
    for (i = 0; i < 4; ++i, window <<= 8) {
Packit 47f805
        int const mw = (mask & window);
Packit 47f805
        int const iw = (id & window);
Packit 47f805
        if (mw != 0 && mw != iw) {
Packit 47f805
            result |= iw;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return result;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
isFrameIdMatching(int id, int mask)
Packit 47f805
{
Packit 47f805
    return frame_id_matches(id, mask) == 0 ? 1 : 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
test_tag_spec_flags(lame_internal_flags const *gfc, unsigned int tst)
Packit 47f805
{
Packit 47f805
    return (gfc->tag_spec.flags & tst) != 0u ? 1 : 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
#if 0
Packit 47f805
static void
Packit 47f805
debug_tag_spec_flags(lame_internal_flags * gfc, const char* info)
Packit 47f805
{
Packit 47f805
    MSGF(gfc, "%s\n", info);
Packit 47f805
    MSGF(gfc, "CHANGED_FLAG  : %d\n", test_tag_spec_flags(gfc, CHANGED_FLAG )); 
Packit 47f805
    MSGF(gfc, "ADD_V2_FLAG   : %d\n", test_tag_spec_flags(gfc, ADD_V2_FLAG  )); 
Packit 47f805
    MSGF(gfc, "V1_ONLY_FLAG  : %d\n", test_tag_spec_flags(gfc, V1_ONLY_FLAG )); 
Packit 47f805
    MSGF(gfc, "V2_ONLY_FLAG  : %d\n", test_tag_spec_flags(gfc, V2_ONLY_FLAG )); 
Packit 47f805
    MSGF(gfc, "SPACE_V1_FLAG : %d\n", test_tag_spec_flags(gfc, SPACE_V1_FLAG)); 
Packit 47f805
    MSGF(gfc, "PAD_V2_FLAG   : %d\n", test_tag_spec_flags(gfc, PAD_V2_FLAG  )); 
Packit 47f805
}
Packit 47f805
#endif
Packit 47f805
Packit 47f805
static int
Packit 47f805
is_lame_internal_flags_null(lame_t gfp)
Packit 47f805
{
Packit 47f805
    return (gfp && gfp->internal_flags) ? 0 : 1;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
id3v2_add_ucs2_lng(lame_t gfp, uint32_t frame_id, unsigned short const *desc, unsigned short const *text);
Packit 47f805
static int
Packit 47f805
id3v2_add_latin1_lng(lame_t gfp, uint32_t frame_id, char const *desc, char const *text);
Packit 47f805
Packit 47f805
Packit 47f805
static void
Packit 47f805
copyV1ToV2(lame_t gfp, int frame_id, char const *s)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    if (gfc != 0) {
Packit 47f805
        unsigned int flags = gfc->tag_spec.flags;
Packit 47f805
        id3v2_add_latin1_lng(gfp, frame_id, 0, s);
Packit 47f805
        gfc->tag_spec.flags = flags;
Packit 47f805
#if 0
Packit 47f805
        debug_tag_spec_flags(gfc, "copyV1ToV2");
Packit 47f805
#endif
Packit 47f805
    }
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static void
Packit 47f805
id3v2AddLameVersion(lame_t gfp)
Packit 47f805
{
Packit 47f805
    char    buffer[1024];
Packit 47f805
    const char *b = get_lame_os_bitness();
Packit 47f805
    const char *v = get_lame_version();
Packit 47f805
    const char *u = get_lame_url();
Packit 47f805
    const size_t lenb = strlen(b);
Packit 47f805
Packit 47f805
    if (lenb > 0) {
Packit 47f805
        sprintf(buffer, "LAME %s version %s (%s)", b, v, u);
Packit 47f805
    }
Packit 47f805
    else {
Packit 47f805
        sprintf(buffer, "LAME version %s (%s)", v, u);
Packit 47f805
    }
Packit 47f805
    copyV1ToV2(gfp, ID_ENCODER, buffer);
Packit 47f805
}
Packit 47f805
Packit 47f805
static void
Packit 47f805
id3v2AddAudioDuration(lame_t gfp, double ms)
Packit 47f805
{
Packit 47f805
    SessionConfig_t const *const cfg = &gfp->internal_flags->cfg; /* caller checked pointers */
Packit 47f805
    char    buffer[1024];
Packit 47f805
    double const max_ulong = MAX_U_32_NUM;
Packit 47f805
    unsigned long playlength_ms;
Packit 47f805
Packit 47f805
    ms *= 1000;
Packit 47f805
    ms /= cfg->samplerate_in;
Packit 47f805
    if (ms > max_ulong) {
Packit 47f805
        playlength_ms = max_ulong;
Packit 47f805
    }
Packit 47f805
    else if (ms < 0) {
Packit 47f805
        playlength_ms = 0;
Packit 47f805
    }
Packit 47f805
    else {
Packit 47f805
        playlength_ms = ms;
Packit 47f805
    }
Packit 47f805
    sprintf(buffer, "%lu", playlength_ms);
Packit 47f805
    copyV1ToV2(gfp, ID_PLAYLENGTH, buffer);
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_genre_list(void (*handler) (int, const char *, void *), void *cookie)
Packit 47f805
{
Packit 47f805
    if (handler) {
Packit 47f805
        int     i;
Packit 47f805
        for (i = 0; i < GENRE_NAME_COUNT; ++i) {
Packit 47f805
            if (i < GENRE_ALPHA_COUNT) {
Packit 47f805
                int     j = genre_alpha_map[i];
Packit 47f805
                handler(j, genre_names[j], cookie);
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
}
Packit 47f805
Packit 47f805
#define GENRE_NUM_UNKNOWN 255
Packit 47f805
Packit 47f805
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_init(lame_t gfp)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = 0;
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
    free_id3tag(gfc);
Packit 47f805
    memset(&gfc->tag_spec, 0, sizeof gfc->tag_spec);
Packit 47f805
    gfc->tag_spec.genre_id3v1 = GENRE_NUM_UNKNOWN;
Packit 47f805
    gfc->tag_spec.padding_size = 128;
Packit 47f805
    id3v2AddLameVersion(gfp);
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_add_v2(lame_t gfp)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = 0;
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
    gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
Packit 47f805
    gfc->tag_spec.flags |= ADD_V2_FLAG;
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_v1_only(lame_t gfp)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = 0;
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
    gfc->tag_spec.flags &= ~(ADD_V2_FLAG | V2_ONLY_FLAG);
Packit 47f805
    gfc->tag_spec.flags |= V1_ONLY_FLAG;
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_v2_only(lame_t gfp)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = 0;
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
    gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
Packit 47f805
    gfc->tag_spec.flags |= V2_ONLY_FLAG;
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_space_v1(lame_t gfp)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = 0;
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
    gfc->tag_spec.flags &= ~V2_ONLY_FLAG;
Packit 47f805
    gfc->tag_spec.flags |= SPACE_V1_FLAG;
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_pad_v2(lame_t gfp)
Packit 47f805
{
Packit 47f805
    id3tag_set_pad(gfp, 128);
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_set_pad(lame_t gfp, size_t n)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = 0;
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
    gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
Packit 47f805
    gfc->tag_spec.flags |= PAD_V2_FLAG;
Packit 47f805
    gfc->tag_spec.flags |= ADD_V2_FLAG;
Packit 47f805
    gfc->tag_spec.padding_size = (unsigned int)n;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
hasUcs2ByteOrderMarker(unsigned short bom)
Packit 47f805
{
Packit 47f805
    if (bom == 0xFFFEu || bom == 0xFEFFu) {
Packit 47f805
        return 1;
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static unsigned short
Packit 47f805
swap_bytes(unsigned short w)
Packit 47f805
{
Packit 47f805
    return (0xff00u & (w << 8)) | (0x00ffu & (w >> 8));
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static unsigned short
Packit 47f805
toLittleEndian(unsigned short bom, unsigned short c)
Packit 47f805
{
Packit 47f805
    if (bom == 0xFFFEu) {
Packit 47f805
        return swap_bytes(c);
Packit 47f805
    }
Packit 47f805
    return c;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned short
Packit 47f805
fromLatin1Char(const unsigned short* s, unsigned short c)
Packit 47f805
{
Packit 47f805
    if (s[0] == 0xFFFEu) {
Packit 47f805
        return swap_bytes(c);
Packit 47f805
    }
Packit 47f805
    return c;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static  size_t
Packit 47f805
local_strdup(char **dst, const char *src)
Packit 47f805
{
Packit 47f805
    if (dst == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    free(*dst);
Packit 47f805
    *dst = 0;
Packit 47f805
    if (src != 0) {
Packit 47f805
        size_t  n;
Packit 47f805
        for (n = 0; src[n] != 0; ++n) { /* calc src string length */
Packit 47f805
        }
Packit 47f805
        if (n > 0) {    /* string length without zero termination */
Packit 47f805
            assert(sizeof(*src) == sizeof(**dst));
Packit 47f805
            *dst = lame_calloc(char, n + 1);
Packit 47f805
            if (*dst != 0) {
Packit 47f805
                memcpy(*dst, src, n * sizeof(**dst));
Packit 47f805
                (*dst)[n] = 0;
Packit 47f805
                return n;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
static  size_t
Packit 47f805
local_ucs2_strdup(unsigned short **dst, unsigned short const *src)
Packit 47f805
{
Packit 47f805
    if (dst == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    free(*dst);         /* free old string pointer */
Packit 47f805
    *dst = 0;
Packit 47f805
    if (src != 0) {
Packit 47f805
        size_t  n;
Packit 47f805
        for (n = 0; src[n] != 0; ++n) { /* calc src string length */
Packit 47f805
        }
Packit 47f805
        if (n > 0) {    /* string length without zero termination */
Packit 47f805
            assert(sizeof(*src) >= 2);
Packit 47f805
            assert(sizeof(*src) == sizeof(**dst));
Packit 47f805
            *dst = lame_calloc(unsigned short, n + 1);
Packit 47f805
            if (*dst != 0) {
Packit 47f805
                memcpy(*dst, src, n * sizeof(**dst));
Packit 47f805
                (*dst)[n] = 0;
Packit 47f805
                return n;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static  size_t
Packit 47f805
local_ucs2_strlen(unsigned short const *s)
Packit 47f805
{
Packit 47f805
    size_t  n = 0;
Packit 47f805
    if (s != 0) {
Packit 47f805
        while (*s++) {
Packit 47f805
            ++n;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return n;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static size_t
Packit 47f805
local_ucs2_substr(unsigned short** dst, unsigned short const* src, size_t start, size_t end)
Packit 47f805
{
Packit 47f805
    size_t const len = 1 + 1 + ((start < end) ? (end - start) : 0);
Packit 47f805
    size_t n = 0;
Packit 47f805
    unsigned short *ptr = lame_calloc(unsigned short, len);
Packit 47f805
    *dst = ptr;
Packit 47f805
    if (ptr == 0 || src == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (hasUcs2ByteOrderMarker(src[0])) {
Packit 47f805
        ptr[n++] = src[0];
Packit 47f805
        if (start == 0) {
Packit 47f805
            ++start;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    while (start < end) {
Packit 47f805
        ptr[n++] = src[start++];
Packit 47f805
    }
Packit 47f805
    ptr[n] = 0;
Packit 47f805
    return n;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
local_ucs2_pos(unsigned short const* str, unsigned short c)
Packit 47f805
{
Packit 47f805
    int     i;
Packit 47f805
    for (i = 0; str != 0 && str[i] != 0; ++i) {
Packit 47f805
        if (str[i] == c) {
Packit 47f805
            return i;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return -1;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
local_char_pos(char const* str, char c)
Packit 47f805
{
Packit 47f805
    int     i;
Packit 47f805
    for (i = 0; str != 0 && str[i] != 0; ++i) {
Packit 47f805
        if (str[i] == c) {
Packit 47f805
            return i;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return -1;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
maybeLatin1(unsigned short const* text)
Packit 47f805
{
Packit 47f805
    if (text) {
Packit 47f805
        unsigned short bom = *text++;
Packit 47f805
        while (*text) {
Packit 47f805
            unsigned short c = toLittleEndian(bom, *text++);
Packit 47f805
            if (c > 0x00fe) return 0;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return 1;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int searchGenre(char const* genre);
Packit 47f805
static int sloppySearchGenre(char const* genre);
Packit 47f805
Packit 47f805
static int
Packit 47f805
lookupGenre(char const* genre)
Packit 47f805
{
Packit 47f805
    char   *str;
Packit 47f805
    int     num = strtol(genre, &str, 10);
Packit 47f805
    /* is the input a string or a valid number? */
Packit 47f805
    if (*str) {
Packit 47f805
        num = searchGenre(genre);
Packit 47f805
        if (num == GENRE_NAME_COUNT) {
Packit 47f805
            num = sloppySearchGenre(genre);
Packit 47f805
        }
Packit 47f805
        if (num == GENRE_NAME_COUNT) {
Packit 47f805
            return -2; /* no common genre text found */
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    else {
Packit 47f805
        if ((num < 0) || (num >= GENRE_NAME_COUNT)) {
Packit 47f805
            return -1; /* number unknown */
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return num;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
writeLoBytes(unsigned char *frame, unsigned short const *str, size_t n);
Packit 47f805
Packit 47f805
static char*
Packit 47f805
local_strdup_utf16_to_latin1(unsigned short const* utf16)
Packit 47f805
{
Packit 47f805
    size_t  len = local_ucs2_strlen(utf16);
Packit 47f805
    unsigned char* latin1 = lame_calloc(unsigned char, len+1);
Packit 47f805
    writeLoBytes(latin1, utf16, len);
Packit 47f805
    return (char*)latin1;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static int
Packit 47f805
id3tag_set_genre_utf16(lame_t gfp, unsigned short const* text)
Packit 47f805
{
Packit 47f805
    lame_internal_flags* gfc = gfp->internal_flags;
Packit 47f805
    int   ret;
Packit 47f805
    if (text == 0) {
Packit 47f805
        return -3;
Packit 47f805
    }
Packit 47f805
    if (!hasUcs2ByteOrderMarker(text[0])) {
Packit 47f805
        return -3;
Packit 47f805
    }
Packit 47f805
    if (maybeLatin1(text)) {
Packit 47f805
        char*   latin1 = local_strdup_utf16_to_latin1(text);
Packit 47f805
        int     num = lookupGenre(latin1);
Packit 47f805
        free(latin1);
Packit 47f805
        if (num == -1) return -1; /* number out of range */
Packit 47f805
        if (num >= 0) {           /* common genre found  */
Packit 47f805
            gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
            gfc->tag_spec.genre_id3v1 = num;
Packit 47f805
            copyV1ToV2(gfp, ID_GENRE, genre_names[num]);
Packit 47f805
            return 0;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    ret = id3v2_add_ucs2_lng(gfp, ID_GENRE, 0, text);
Packit 47f805
    if (ret == 0) {
Packit 47f805
        gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
        gfc->tag_spec.genre_id3v1 = GENRE_INDEX_OTHER;
Packit 47f805
    }
Packit 47f805
    return ret;
Packit 47f805
}
Packit 47f805
Packit 47f805
/*
Packit 47f805
Some existing options for ID3 tag can be specified by --tv option
Packit 47f805
as follows.
Packit 47f805
--tt <value>, --tv TIT2=value
Packit 47f805
--ta <value>, --tv TPE1=value
Packit 47f805
--tl <value>, --tv TALB=value
Packit 47f805
--ty <value>, --tv TYER=value
Packit 47f805
--tn <value>, --tv TRCK=value
Packit 47f805
--tg <value>, --tv TCON=value
Packit 47f805
(although some are not exactly same)*/
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_albumart(lame_t gfp, const char *image, size_t size)
Packit 47f805
{
Packit 47f805
    int     mimetype = MIMETYPE_NONE;
Packit 47f805
    lame_internal_flags *gfc = 0;
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
Packit 47f805
    if (image != 0) {
Packit 47f805
        unsigned char const *data = (unsigned char const *) image;
Packit 47f805
        /* determine MIME type from the actual image data */
Packit 47f805
        if (2 < size && data[0] == 0xFF && data[1] == 0xD8) {
Packit 47f805
            mimetype = MIMETYPE_JPEG;
Packit 47f805
        }
Packit 47f805
        else if (4 < size && data[0] == 0x89 && strncmp((const char *) &data[1], "PNG", 3) == 0) {
Packit 47f805
            mimetype = MIMETYPE_PNG;
Packit 47f805
        }
Packit 47f805
        else if (4 < size && strncmp((const char *) data, "GIF8", 4) == 0) {
Packit 47f805
            mimetype = MIMETYPE_GIF;
Packit 47f805
        }
Packit 47f805
        else {
Packit 47f805
            return -1;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    if (gfc->tag_spec.albumart != 0) {
Packit 47f805
        free(gfc->tag_spec.albumart);
Packit 47f805
        gfc->tag_spec.albumart = 0;
Packit 47f805
        gfc->tag_spec.albumart_size = 0;
Packit 47f805
        gfc->tag_spec.albumart_mimetype = MIMETYPE_NONE;
Packit 47f805
    }
Packit 47f805
    if (size < 1 || mimetype == MIMETYPE_NONE) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    gfc->tag_spec.albumart = lame_calloc(unsigned char, size);
Packit 47f805
    if (gfc->tag_spec.albumart != 0) {
Packit 47f805
        memcpy(gfc->tag_spec.albumart, image, size);
Packit 47f805
        gfc->tag_spec.albumart_size = (unsigned int)size;
Packit 47f805
        gfc->tag_spec.albumart_mimetype = mimetype;
Packit 47f805
        gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
        id3tag_add_v2(gfp);
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
set_4_byte_value(unsigned char *bytes, uint32_t value)
Packit 47f805
{
Packit 47f805
    int     i;
Packit 47f805
    for (i = 3; i >= 0; --i) {
Packit 47f805
        bytes[i] = value & 0xffUL;
Packit 47f805
        value >>= 8;
Packit 47f805
    }
Packit 47f805
    return bytes + 4;
Packit 47f805
}
Packit 47f805
Packit 47f805
static uint32_t
Packit 47f805
toID3v2TagId(char const *s)
Packit 47f805
{
Packit 47f805
    unsigned int i, x = 0;
Packit 47f805
    if (s == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    for (i = 0; i < 4 && s[i] != 0; ++i) {
Packit 47f805
        char const c = s[i];
Packit 47f805
        unsigned int const u = 0x0ff & c;
Packit 47f805
        x <<= 8;
Packit 47f805
        x |= u;
Packit 47f805
        if (c < 'A' || 'Z' < c) {
Packit 47f805
            if (c < '0' || '9' < c) {
Packit 47f805
                return 0;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return x;
Packit 47f805
}
Packit 47f805
Packit 47f805
static uint32_t
Packit 47f805
toID3v2TagId_ucs2(unsigned short const *s)
Packit 47f805
{
Packit 47f805
    unsigned int i, x = 0;
Packit 47f805
    unsigned short bom = 0;
Packit 47f805
    if (s == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    bom = s[0];
Packit 47f805
    if (hasUcs2ByteOrderMarker(bom)) {
Packit 47f805
        ++s;
Packit 47f805
    }
Packit 47f805
    for (i = 0; i < 4 && s[i] != 0; ++i) {
Packit 47f805
        unsigned short const c = toLittleEndian(bom, s[i]);
Packit 47f805
        if (c < 'A' || 'Z' < c) {
Packit 47f805
            if (c < '0' || '9' < c) {
Packit 47f805
                return 0;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
        x <<= 8;
Packit 47f805
        x |= c;
Packit 47f805
    }
Packit 47f805
    return x;
Packit 47f805
}
Packit 47f805
Packit 47f805
#if 0
Packit 47f805
static int
Packit 47f805
isNumericString(uint32_t frame_id)
Packit 47f805
{
Packit 47f805
    switch (frame_id) {
Packit 47f805
    case ID_DATE:
Packit 47f805
    case ID_TIME:
Packit 47f805
    case ID_TPOS:
Packit 47f805
    case ID_TRACK:
Packit 47f805
    case ID_YEAR:
Packit 47f805
        return 1;
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
#endif
Packit 47f805
Packit 47f805
static int
Packit 47f805
isMultiFrame(uint32_t frame_id)
Packit 47f805
{
Packit 47f805
    switch (frame_id) {
Packit 47f805
    case ID_TXXX:
Packit 47f805
    case ID_WXXX:
Packit 47f805
    case ID_COMMENT:
Packit 47f805
    case ID_SYLT:
Packit 47f805
    case ID_APIC:
Packit 47f805
    case ID_GEOB:
Packit 47f805
    case ID_PCNT:
Packit 47f805
    case ID_AENC:
Packit 47f805
    case ID_LINK:
Packit 47f805
    case ID_ENCR:
Packit 47f805
    case ID_GRID:
Packit 47f805
    case ID_PRIV:
Packit 47f805
        return 1;
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
#if 0
Packit 47f805
static int
Packit 47f805
isFullTextString(int frame_id)
Packit 47f805
{
Packit 47f805
    switch (frame_id) {
Packit 47f805
    case ID_VSLT:
Packit 47f805
    case ID_COMMENT:
Packit 47f805
        return 1;
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
#endif
Packit 47f805
Packit 47f805
static FrameDataNode *
Packit 47f805
findNode(id3tag_spec const *tag, uint32_t frame_id, FrameDataNode const *last)
Packit 47f805
{
Packit 47f805
    FrameDataNode *node = last ? last->nxt : tag->v2_head;
Packit 47f805
    while (node != 0) {
Packit 47f805
        if (node->fid == frame_id) {
Packit 47f805
            return node;
Packit 47f805
        }
Packit 47f805
        node = node->nxt;
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
static void
Packit 47f805
appendNode(id3tag_spec * tag, FrameDataNode * node)
Packit 47f805
{
Packit 47f805
    if (tag->v2_tail == 0 || tag->v2_head == 0) {
Packit 47f805
        tag->v2_head = node;
Packit 47f805
        tag->v2_tail = node;
Packit 47f805
    }
Packit 47f805
    else {
Packit 47f805
        tag->v2_tail->nxt = node;
Packit 47f805
        tag->v2_tail = node;
Packit 47f805
    }
Packit 47f805
}
Packit 47f805
Packit 47f805
static void
Packit 47f805
setLang(char *dst, char const *src)
Packit 47f805
{
Packit 47f805
    int     i;
Packit 47f805
    if (src == 0 || src[0] == 0) {
Packit 47f805
        dst[0] = 'e';
Packit 47f805
        dst[1] = 'n';
Packit 47f805
        dst[2] = 'g';
Packit 47f805
    }
Packit 47f805
    else {
Packit 47f805
        for (i = 0; i < 3 && src && *src; ++i) {
Packit 47f805
            dst[i] = src[i];
Packit 47f805
        }
Packit 47f805
        for (; i < 3; ++i) {
Packit 47f805
            dst[i] = ' ';
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
isSameLang(char const *l1, char const *l2)
Packit 47f805
{
Packit 47f805
    char    d[3];
Packit 47f805
    int     i;
Packit 47f805
    setLang(d, l2);
Packit 47f805
    for (i = 0; i < 3; ++i) {
Packit 47f805
        char    a = tolower(l1[i]);
Packit 47f805
        char    b = tolower(d[i]);
Packit 47f805
        if (a < ' ')
Packit 47f805
            a = ' ';
Packit 47f805
        if (b < ' ')
Packit 47f805
            b = ' ';
Packit 47f805
        if (a != b) {
Packit 47f805
            return 0;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return 1;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
isSameDescriptor(FrameDataNode const *node, char const *dsc)
Packit 47f805
{
Packit 47f805
    size_t  i;
Packit 47f805
    if (node->dsc.enc == 1 && node->dsc.dim > 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    for (i = 0; i < node->dsc.dim; ++i) {
Packit 47f805
        if (!dsc || node->dsc.ptr.l[i] != dsc[i]) {
Packit 47f805
            return 0;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return 1;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
isSameDescriptorUcs2(FrameDataNode const *node, unsigned short const *dsc)
Packit 47f805
{
Packit 47f805
    size_t  i;
Packit 47f805
    if (node->dsc.enc != 1 && node->dsc.dim > 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    for (i = 0; i < node->dsc.dim; ++i) {
Packit 47f805
        if (!dsc || node->dsc.ptr.u[i] != dsc[i]) {
Packit 47f805
            return 0;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return 1;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
id3v2_add_ucs2(lame_t gfp, uint32_t frame_id, char const *lng, unsigned short const *desc, unsigned short const *text)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    if (gfc != 0) {
Packit 47f805
        FrameDataNode *node = findNode(&gfc->tag_spec, frame_id, 0);
Packit 47f805
        char lang[4];
Packit 47f805
        setLang(lang, lng);
Packit 47f805
        if (isMultiFrame(frame_id)) {
Packit 47f805
            while (node) {
Packit 47f805
                if (isSameLang(node->lng, lang)) {
Packit 47f805
                    if (isSameDescriptorUcs2(node, desc)) {
Packit 47f805
                        break;
Packit 47f805
                    }
Packit 47f805
                }
Packit 47f805
                node = findNode(&gfc->tag_spec, frame_id, node);
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
        if (node == 0) {
Packit 47f805
            node = lame_calloc(FrameDataNode, 1);
Packit 47f805
            if (node == 0) {
Packit 47f805
                return -254; /* memory problem */
Packit 47f805
            }
Packit 47f805
            appendNode(&gfc->tag_spec, node);
Packit 47f805
        }
Packit 47f805
        node->fid = frame_id;
Packit 47f805
        setLang(node->lng, lang);
Packit 47f805
        node->dsc.dim = local_ucs2_strdup(&node->dsc.ptr.u, desc);
Packit 47f805
        node->dsc.enc = 1;
Packit 47f805
        node->txt.dim = local_ucs2_strdup(&node->txt.ptr.u, text);
Packit 47f805
        node->txt.enc = 1;
Packit 47f805
        gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    return -255;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
id3v2_add_latin1(lame_t gfp, uint32_t frame_id, char const *lng, char const *desc, char const *text)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    if (gfc != 0) {
Packit 47f805
        FrameDataNode *node = findNode(&gfc->tag_spec, frame_id, 0);
Packit 47f805
        char lang[4];
Packit 47f805
        setLang(lang, lng);
Packit 47f805
        if (isMultiFrame(frame_id)) {
Packit 47f805
            while (node) {
Packit 47f805
                if (isSameLang(node->lng, lang)) {
Packit 47f805
                    if (isSameDescriptor(node, desc)) {
Packit 47f805
                        break;
Packit 47f805
                    }
Packit 47f805
                }
Packit 47f805
                node = findNode(&gfc->tag_spec, frame_id, node);
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
        if (node == 0) {
Packit 47f805
            node = lame_calloc(FrameDataNode, 1);
Packit 47f805
            if (node == 0) {
Packit 47f805
                return -254; /* memory problem */
Packit 47f805
            }
Packit 47f805
            appendNode(&gfc->tag_spec, node);
Packit 47f805
        }
Packit 47f805
        node->fid = frame_id;
Packit 47f805
        setLang(node->lng, lang);
Packit 47f805
        node->dsc.dim = local_strdup(&node->dsc.ptr.l, desc);
Packit 47f805
        node->dsc.enc = 0;
Packit 47f805
        node->txt.dim = local_strdup(&node->txt.ptr.l, text);
Packit 47f805
        node->txt.enc = 0;
Packit 47f805
        gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    return -255;
Packit 47f805
}
Packit 47f805
Packit 47f805
static char const*
Packit 47f805
id3v2_get_language(lame_t gfp)
Packit 47f805
{
Packit 47f805
    lame_internal_flags const* gfc = gfp ? gfp->internal_flags : 0;
Packit 47f805
    if (gfc) return gfc->tag_spec.language;
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
id3v2_add_ucs2_lng(lame_t gfp, uint32_t frame_id, unsigned short const *desc, unsigned short const *text)
Packit 47f805
{
Packit 47f805
    char const* lang = id3v2_get_language(gfp);
Packit 47f805
    return id3v2_add_ucs2(gfp, frame_id, lang, desc, text);
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
id3v2_add_latin1_lng(lame_t gfp, uint32_t frame_id, char const *desc, char const *text)
Packit 47f805
{
Packit 47f805
    char const* lang = id3v2_get_language(gfp);
Packit 47f805
    return id3v2_add_latin1(gfp, frame_id, lang, desc, text);
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
id3tag_set_userinfo_latin1(lame_t gfp, uint32_t id, char const *fieldvalue)
Packit 47f805
{
Packit 47f805
    char const separator = '=';
Packit 47f805
    int     rc = -7;
Packit 47f805
    int     a = local_char_pos(fieldvalue, separator);
Packit 47f805
    if (a >= 0) {
Packit 47f805
        char*   dup = 0;
Packit 47f805
        local_strdup(&dup, fieldvalue);
Packit 47f805
        dup[a] = 0;
Packit 47f805
        rc = id3v2_add_latin1_lng(gfp, id, dup, dup+a+1);
Packit 47f805
        free(dup);
Packit 47f805
    }
Packit 47f805
    return rc;
Packit 47f805
}
Packit 47f805
Packit 47f805
static int
Packit 47f805
id3tag_set_userinfo_ucs2(lame_t gfp, uint32_t id, unsigned short const *fieldvalue)
Packit 47f805
{
Packit 47f805
    unsigned short const separator = fromLatin1Char(fieldvalue,'=');
Packit 47f805
    int     rc = -7;
Packit 47f805
    size_t  b = local_ucs2_strlen(fieldvalue);
Packit 47f805
    int     a = local_ucs2_pos(fieldvalue, separator);
Packit 47f805
    if (a >= 0) {
Packit 47f805
        unsigned short* dsc = 0, *val = 0;
Packit 47f805
        local_ucs2_substr(&dsc, fieldvalue, 0, a);
Packit 47f805
        local_ucs2_substr(&val, fieldvalue, a+1, b);
Packit 47f805
        rc = id3v2_add_ucs2_lng(gfp, id, dsc, val);
Packit 47f805
        free(dsc);
Packit 47f805
        free(val);
Packit 47f805
    }
Packit 47f805
    return rc;
Packit 47f805
}
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text)
Packit 47f805
{
Packit 47f805
    uint32_t const frame_id = toID3v2TagId(id);
Packit 47f805
    if (frame_id == 0) {
Packit 47f805
        return -1;
Packit 47f805
    }
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (text == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (!hasUcs2ByteOrderMarker(text[0])) {
Packit 47f805
        return -3;  /* BOM missing */
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_TXXX || frame_id == ID_WXXX || frame_id == ID_COMMENT) {
Packit 47f805
        return id3tag_set_userinfo_ucs2(gfp, frame_id, text);
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_GENRE) {
Packit 47f805
        return id3tag_set_genre_utf16(gfp, text);
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_PCST) {
Packit 47f805
        return id3v2_add_ucs2_lng(gfp, frame_id, 0, text);
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_USER) {
Packit 47f805
        return id3v2_add_ucs2_lng(gfp, frame_id, text, 0);
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_WFED) {
Packit 47f805
        return id3v2_add_ucs2_lng(gfp, frame_id, text, 0); /* iTunes expects WFED to be a text frame */
Packit 47f805
    }
Packit 47f805
    if (isFrameIdMatching(frame_id, FRAME_ID('T', 0, 0, 0))
Packit 47f805
      ||isFrameIdMatching(frame_id, FRAME_ID('W', 0, 0, 0))) {
Packit 47f805
#if 0
Packit 47f805
        if (isNumericString(frame_id)) {
Packit 47f805
            return -2;  /* must be Latin-1 encoded */
Packit 47f805
        }
Packit 47f805
#endif
Packit 47f805
        return id3v2_add_ucs2_lng(gfp, frame_id, 0, text);
Packit 47f805
    }
Packit 47f805
    return -255;        /* not supported by now */
Packit 47f805
}
Packit 47f805
Packit 47f805
extern int
Packit 47f805
id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text);
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text)
Packit 47f805
{
Packit 47f805
    return id3tag_set_textinfo_utf16(gfp, id, text);
Packit 47f805
}
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text)
Packit 47f805
{
Packit 47f805
    uint32_t const frame_id = toID3v2TagId(id);
Packit 47f805
    if (frame_id == 0) {
Packit 47f805
        return -1;
Packit 47f805
    }
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (text == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_TXXX || frame_id == ID_WXXX || frame_id == ID_COMMENT) {
Packit 47f805
        return id3tag_set_userinfo_latin1(gfp, frame_id, text);
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_GENRE) {
Packit 47f805
        return id3tag_set_genre(gfp, text);
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_PCST) {
Packit 47f805
        return id3v2_add_latin1_lng(gfp, frame_id, 0, text);
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_USER) {
Packit 47f805
        return id3v2_add_latin1_lng(gfp, frame_id, text, 0);
Packit 47f805
    }
Packit 47f805
    if (frame_id == ID_WFED) {
Packit 47f805
        return id3v2_add_latin1_lng(gfp, frame_id, text, 0); /* iTunes expects WFED to be a text frame */
Packit 47f805
    }
Packit 47f805
    if (isFrameIdMatching(frame_id, FRAME_ID('T', 0, 0, 0))
Packit 47f805
      ||isFrameIdMatching(frame_id, FRAME_ID('W', 0, 0, 0))) {
Packit 47f805
        return id3v2_add_latin1_lng(gfp, frame_id, 0, text);
Packit 47f805
    }
Packit 47f805
    return -255;        /* not supported by now */
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text)
Packit 47f805
{
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    return id3v2_add_latin1(gfp, ID_COMMENT, lang, desc, text);
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text)
Packit 47f805
{
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    return id3v2_add_ucs2(gfp, ID_COMMENT, lang, desc, text);
Packit 47f805
}
Packit 47f805
Packit 47f805
extern int
Packit 47f805
id3tag_set_comment_ucs2(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text);
Packit 47f805
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_comment_ucs2(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text)
Packit 47f805
{
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    return id3tag_set_comment_utf16(gfp, lang, desc, text);
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_set_title(lame_t gfp, const char *title)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    if (gfc && title && *title) {
Packit 47f805
        local_strdup(&gfc->tag_spec.title, title);
Packit 47f805
        gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
        copyV1ToV2(gfp, ID_TITLE, title);
Packit 47f805
    }
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_set_artist(lame_t gfp, const char *artist)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    if (gfc && artist && *artist) {
Packit 47f805
        local_strdup(&gfc->tag_spec.artist, artist);
Packit 47f805
        gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
        copyV1ToV2(gfp, ID_ARTIST, artist);
Packit 47f805
    }
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_set_album(lame_t gfp, const char *album)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    if (gfc && album && *album) {
Packit 47f805
        local_strdup(&gfc->tag_spec.album, album);
Packit 47f805
        gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
        copyV1ToV2(gfp, ID_ALBUM, album);
Packit 47f805
    }
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_set_year(lame_t gfp, const char *year)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    if (gfc && year && *year) {
Packit 47f805
        int     num = atoi(year);
Packit 47f805
        if (num < 0) {
Packit 47f805
            num = 0;
Packit 47f805
        }
Packit 47f805
        /* limit a year to 4 digits so it fits in a version 1 tag */
Packit 47f805
        if (num > 9999) {
Packit 47f805
            num = 9999;
Packit 47f805
        }
Packit 47f805
        if (num) {
Packit 47f805
            gfc->tag_spec.year = num;
Packit 47f805
            gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
        }
Packit 47f805
        copyV1ToV2(gfp, ID_YEAR, year);
Packit 47f805
    }
Packit 47f805
}
Packit 47f805
Packit 47f805
void
Packit 47f805
id3tag_set_comment(lame_t gfp, const char *comment)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    if (gfc && comment && *comment) {
Packit 47f805
        local_strdup(&gfc->tag_spec.comment, comment);
Packit 47f805
        gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
        {
Packit 47f805
            uint32_t const flags = gfc->tag_spec.flags;
Packit 47f805
            id3v2_add_latin1_lng(gfp, ID_COMMENT, "", comment);
Packit 47f805
            gfc->tag_spec.flags = flags;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
}
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_track(lame_t gfp, const char *track)
Packit 47f805
{
Packit 47f805
    char const *trackcount;
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    int     ret = 0;
Packit 47f805
Packit 47f805
    if (gfc && track && *track) {
Packit 47f805
        int     num = atoi(track);
Packit 47f805
        /* check for valid ID3v1 track number range */
Packit 47f805
        if (num < 1 || num > 255) {
Packit 47f805
            num = 0;
Packit 47f805
            ret = -1;   /* track number out of ID3v1 range, ignored for ID3v1 */
Packit 47f805
            gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
Packit 47f805
        }
Packit 47f805
        if (num) {
Packit 47f805
            gfc->tag_spec.track_id3v1 = num;
Packit 47f805
            gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
        }
Packit 47f805
        /* Look for the total track count after a "/", same restrictions */
Packit 47f805
        trackcount = strchr(track, '/');
Packit 47f805
        if (trackcount && *trackcount) {
Packit 47f805
            gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
Packit 47f805
        }
Packit 47f805
        copyV1ToV2(gfp, ID_TRACK, track);
Packit 47f805
    }
Packit 47f805
    return ret;
Packit 47f805
}
Packit 47f805
Packit 47f805
/* would use real "strcasecmp" but it isn't portable */
Packit 47f805
static int
Packit 47f805
local_strcasecmp(const char *s1, const char *s2)
Packit 47f805
{
Packit 47f805
    unsigned char c1;
Packit 47f805
    unsigned char c2;
Packit 47f805
    do {
Packit 47f805
        c1 = tolower(*s1);
Packit 47f805
        c2 = tolower(*s2);
Packit 47f805
        if (!c1) {
Packit 47f805
            break;
Packit 47f805
        }
Packit 47f805
        ++s1;
Packit 47f805
        ++s2;
Packit 47f805
    } while (c1 == c2);
Packit 47f805
    return c1 - c2;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static 
Packit 47f805
const char* nextUpperAlpha(const char* p, char x)
Packit 47f805
{
Packit 47f805
    char c;
Packit 47f805
    for(c = toupper(*p); *p != 0; c = toupper(*++p)) {
Packit 47f805
        if ('A' <= c && c <= 'Z') {
Packit 47f805
            if (c != x) {
Packit 47f805
                return p;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return p;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static int
Packit 47f805
sloppyCompared(const char* p, const char* q)
Packit 47f805
{
Packit 47f805
    char cp, cq;
Packit 47f805
    p = nextUpperAlpha(p, 0);
Packit 47f805
    q = nextUpperAlpha(q, 0);
Packit 47f805
    cp = toupper(*p);
Packit 47f805
    cq = toupper(*q);
Packit 47f805
    while (cp == cq) {
Packit 47f805
        if (cp == 0) {
Packit 47f805
            return 1;
Packit 47f805
        }
Packit 47f805
        if (p[1] == '.') { /* some abbrevation */
Packit 47f805
            while (*q && *q++ != ' ') {
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
        p = nextUpperAlpha(p, cp);
Packit 47f805
        q = nextUpperAlpha(q, cq);
Packit 47f805
        cp = toupper(*p);
Packit 47f805
        cq = toupper(*q);
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static int 
Packit 47f805
sloppySearchGenre(const char *genre)
Packit 47f805
{
Packit 47f805
    int i;
Packit 47f805
    for (i = 0; i < GENRE_NAME_COUNT; ++i) {
Packit 47f805
        if (sloppyCompared(genre, genre_names[i])) {
Packit 47f805
            return i;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return GENRE_NAME_COUNT;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static int
Packit 47f805
searchGenre(const char* genre)
Packit 47f805
{
Packit 47f805
    int i;
Packit 47f805
    for (i = 0; i < GENRE_NAME_COUNT; ++i) {
Packit 47f805
        if (!local_strcasecmp(genre, genre_names[i])) {
Packit 47f805
            return i;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return GENRE_NAME_COUNT;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_genre(lame_t gfp, const char *genre)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
Packit 47f805
    int     ret = 0;
Packit 47f805
    if (gfc && genre && *genre) {
Packit 47f805
        int const num = lookupGenre(genre);
Packit 47f805
        if (num == -1) return num;
Packit 47f805
        gfc->tag_spec.flags |= CHANGED_FLAG;
Packit 47f805
        if (num >= 0) {
Packit 47f805
            gfc->tag_spec.genre_id3v1 = num;
Packit 47f805
            genre = genre_names[num];
Packit 47f805
        }
Packit 47f805
        else {
Packit 47f805
            gfc->tag_spec.genre_id3v1 = GENRE_INDEX_OTHER;
Packit 47f805
            gfc->tag_spec.flags |= ADD_V2_FLAG;
Packit 47f805
        }
Packit 47f805
        copyV1ToV2(gfp, ID_GENRE, genre);
Packit 47f805
    }
Packit 47f805
    return ret;
Packit 47f805
}
Packit 47f805
Packit 47f805
Packit 47f805
static  size_t
Packit 47f805
sizeOfNode(FrameDataNode const *node)
Packit 47f805
{
Packit 47f805
    size_t  n = 0;
Packit 47f805
    if (node) {
Packit 47f805
        n = 10;         /* header size */
Packit 47f805
        n += 1;         /* text encoding flag */
Packit 47f805
        switch (node->txt.enc) {
Packit 47f805
        default:
Packit 47f805
        case 0:
Packit 47f805
            if (node->dsc.dim > 0) {
Packit 47f805
                n += node->dsc.dim + 1;
Packit 47f805
            }
Packit 47f805
            n += node->txt.dim;
Packit 47f805
            break;
Packit 47f805
        case 1:
Packit 47f805
            if (node->dsc.dim > 0) {
Packit 47f805
                n += (node->dsc.dim+1) * 2;
Packit 47f805
            }
Packit 47f805
            n += node->txt.dim * 2;
Packit 47f805
            break;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return n;
Packit 47f805
}
Packit 47f805
Packit 47f805
static  size_t
Packit 47f805
sizeOfCommentNode(FrameDataNode const *node)
Packit 47f805
{
Packit 47f805
    size_t  n = 0;
Packit 47f805
    if (node) {
Packit 47f805
        n = 10;         /* header size */
Packit 47f805
        n += 1;         /* text encoding flag */
Packit 47f805
        n += 3;         /* language */
Packit 47f805
        switch (node->dsc.enc) {
Packit 47f805
        default:
Packit 47f805
        case 0:
Packit 47f805
            n += 1 + node->dsc.dim;
Packit 47f805
            break;
Packit 47f805
        case 1:
Packit 47f805
            n += 2 + node->dsc.dim * 2;
Packit 47f805
            break;
Packit 47f805
        }
Packit 47f805
        switch (node->txt.enc) {
Packit 47f805
        default:
Packit 47f805
        case 0:
Packit 47f805
            n += node->txt.dim;
Packit 47f805
            break;
Packit 47f805
        case 1:
Packit 47f805
            n += node->txt.dim * 2;
Packit 47f805
            break;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return n;
Packit 47f805
}
Packit 47f805
Packit 47f805
static size_t
Packit 47f805
sizeOfWxxxNode(FrameDataNode const *node)
Packit 47f805
{
Packit 47f805
    size_t  n = 0;
Packit 47f805
    if (node) {
Packit 47f805
        n = 10;         /* header size */
Packit 47f805
        if (node->dsc.dim > 0) {
Packit 47f805
            n += 1;         /* text encoding flag */
Packit 47f805
            switch (node->dsc.enc) {
Packit 47f805
            default:
Packit 47f805
            case 0:
Packit 47f805
                n += 1 + node->dsc.dim;
Packit 47f805
                break;
Packit 47f805
            case 1:
Packit 47f805
                n += 2 + node->dsc.dim * 2;
Packit 47f805
                break;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
        if (node->txt.dim > 0) {
Packit 47f805
            switch (node->txt.enc) {
Packit 47f805
            default:
Packit 47f805
            case 0:
Packit 47f805
                n += node->txt.dim;
Packit 47f805
                break;
Packit 47f805
            case 1:
Packit 47f805
                n += node->txt.dim - 1; /* UCS2 -> Latin1, skip BOM */
Packit 47f805
                break;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return n;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
writeChars(unsigned char *frame, char const *str, size_t n)
Packit 47f805
{
Packit 47f805
    while (n--) {
Packit 47f805
        *frame++ = *str++;
Packit 47f805
    }
Packit 47f805
    return frame;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
writeUcs2s(unsigned char *frame, unsigned short const *str, size_t n)
Packit 47f805
{
Packit 47f805
    if (n > 0) {
Packit 47f805
        unsigned short const bom = *str;
Packit 47f805
        while (n--) {
Packit 47f805
            unsigned short const c = toLittleEndian(bom, *str++);
Packit 47f805
            *frame++ = 0x00ffu & c;
Packit 47f805
            *frame++ = 0x00ffu & (c >> 8);
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return frame;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
writeLoBytes(unsigned char *frame, unsigned short const *str, size_t n)
Packit 47f805
{
Packit 47f805
    if (n > 0) {
Packit 47f805
        unsigned short const bom = *str;
Packit 47f805
        if (hasUcs2ByteOrderMarker(bom)) {
Packit 47f805
            str++; n--; /* skip BOM */
Packit 47f805
        }
Packit 47f805
        while (n--) {
Packit 47f805
            unsigned short const c = toLittleEndian(bom, *str++);
Packit 47f805
            if (c < 0x0020u || 0x00ffu < c) {
Packit 47f805
                *frame++ = 0x0020; /* blank */
Packit 47f805
            }
Packit 47f805
            else {
Packit 47f805
                *frame++ = c;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return frame;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
set_frame_comment(unsigned char *frame, FrameDataNode const *node)
Packit 47f805
{
Packit 47f805
    size_t const n = sizeOfCommentNode(node);
Packit 47f805
    if (n > 10) {
Packit 47f805
        frame = set_4_byte_value(frame, node->fid);
Packit 47f805
        frame = set_4_byte_value(frame, (uint32_t) (n - 10));
Packit 47f805
        /* clear 2-byte header flags */
Packit 47f805
        *frame++ = 0;
Packit 47f805
        *frame++ = 0;
Packit 47f805
        /* encoding descriptor byte */
Packit 47f805
        *frame++ = node->txt.enc == 1 ? 1 : 0;
Packit 47f805
        /* 3 bytes language */
Packit 47f805
        *frame++ = node->lng[0];
Packit 47f805
        *frame++ = node->lng[1];
Packit 47f805
        *frame++ = node->lng[2];
Packit 47f805
        /* descriptor with zero byte(s) separator */
Packit 47f805
        if (node->dsc.enc != 1) {
Packit 47f805
            frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
Packit 47f805
            *frame++ = 0;
Packit 47f805
        }
Packit 47f805
        else {
Packit 47f805
            frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
Packit 47f805
            *frame++ = 0;
Packit 47f805
            *frame++ = 0;
Packit 47f805
        }
Packit 47f805
        /* comment full text */
Packit 47f805
        if (node->txt.enc != 1) {
Packit 47f805
            frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
Packit 47f805
        }
Packit 47f805
        else {
Packit 47f805
            frame = writeUcs2s(frame, node->txt.ptr.u, node->txt.dim);
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return frame;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
set_frame_custom2(unsigned char *frame, FrameDataNode const *node)
Packit 47f805
{
Packit 47f805
    size_t const n = sizeOfNode(node);
Packit 47f805
    if (n > 10) {
Packit 47f805
        frame = set_4_byte_value(frame, node->fid);
Packit 47f805
        frame = set_4_byte_value(frame, (unsigned long) (n - 10));
Packit 47f805
        /* clear 2-byte header flags */
Packit 47f805
        *frame++ = 0;
Packit 47f805
        *frame++ = 0;
Packit 47f805
        /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
Packit 47f805
        *frame++ = node->txt.enc == 1 ? 1 : 0;
Packit 47f805
        if (node->dsc.dim > 0) {
Packit 47f805
            if (node->dsc.enc != 1) {
Packit 47f805
                frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
Packit 47f805
                *frame++ = 0;
Packit 47f805
            }
Packit 47f805
            else {
Packit 47f805
                frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
Packit 47f805
                *frame++ = 0;
Packit 47f805
                *frame++ = 0;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
        if (node->txt.enc != 1) {
Packit 47f805
            frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
Packit 47f805
        }
Packit 47f805
        else {
Packit 47f805
            frame = writeUcs2s(frame, node->txt.ptr.u, node->txt.dim);
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return frame;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
set_frame_wxxx(unsigned char *frame, FrameDataNode const *node)
Packit 47f805
{
Packit 47f805
    size_t const n = sizeOfWxxxNode(node);
Packit 47f805
    if (n > 10) {
Packit 47f805
        frame = set_4_byte_value(frame, node->fid);
Packit 47f805
        frame = set_4_byte_value(frame, (unsigned long) (n - 10));
Packit 47f805
        /* clear 2-byte header flags */
Packit 47f805
        *frame++ = 0;
Packit 47f805
        *frame++ = 0;
Packit 47f805
        if (node->dsc.dim > 0) {
Packit 47f805
            /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
Packit 47f805
            *frame++ = node->dsc.enc == 1 ? 1 : 0;
Packit 47f805
            if (node->dsc.enc != 1) {
Packit 47f805
                frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
Packit 47f805
                *frame++ = 0;
Packit 47f805
            }
Packit 47f805
            else {
Packit 47f805
                frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
Packit 47f805
                *frame++ = 0;
Packit 47f805
                *frame++ = 0;
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
        if (node->txt.enc != 1) {
Packit 47f805
            frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
Packit 47f805
        }
Packit 47f805
        else {
Packit 47f805
            frame = writeLoBytes(frame, node->txt.ptr.u, node->txt.dim);
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return frame;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
set_frame_apic(unsigned char *frame, const char *mimetype, const unsigned char *data, size_t size)
Packit 47f805
{
Packit 47f805
    /* ID3v2.3 standard APIC frame:
Packit 47f805
     *     <Header for 'Attached picture', ID: "APIC">
Packit 47f805
     *     Text encoding    $xx
Packit 47f805
     *     MIME type        <text string> $00
Packit 47f805
     *     Picture type     $xx
Packit 47f805
     *     Description      <text string according to encoding> $00 (00)
Packit 47f805
     *     Picture data     <binary data>
Packit 47f805
     */
Packit 47f805
    if (mimetype && data && size) {
Packit 47f805
        frame = set_4_byte_value(frame, FRAME_ID('A', 'P', 'I', 'C'));
Packit 47f805
        frame = set_4_byte_value(frame, (unsigned long) (4 + strlen(mimetype) + size));
Packit 47f805
        /* clear 2-byte header flags */
Packit 47f805
        *frame++ = 0;
Packit 47f805
        *frame++ = 0;
Packit 47f805
        /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
Packit 47f805
        *frame++ = 0;
Packit 47f805
        /* copy mime_type */
Packit 47f805
        while (*mimetype) {
Packit 47f805
            *frame++ = *mimetype++;
Packit 47f805
        }
Packit 47f805
        *frame++ = 0;
Packit 47f805
        /* set picture type to 0 */
Packit 47f805
        *frame++ = 0;
Packit 47f805
        /* empty description field */
Packit 47f805
        *frame++ = 0;
Packit 47f805
        /* copy the image data */
Packit 47f805
        while (size--) {
Packit 47f805
            *frame++ = *data++;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return frame;
Packit 47f805
}
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_fieldvalue(lame_t gfp, const char *fieldvalue)
Packit 47f805
{
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (fieldvalue && *fieldvalue) {
Packit 47f805
        if (strlen(fieldvalue) < 5 || fieldvalue[4] != '=') {
Packit 47f805
            return -1;
Packit 47f805
        }
Packit 47f805
        return id3tag_set_textinfo_latin1(gfp, fieldvalue, &fieldvalue[5]);
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue)
Packit 47f805
{
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (fieldvalue && *fieldvalue) {
Packit 47f805
        size_t dx = hasUcs2ByteOrderMarker(fieldvalue[0]);
Packit 47f805
        unsigned short const separator = fromLatin1Char(fieldvalue, '=');
Packit 47f805
        char fid[5] = {0,0,0,0,0};
Packit 47f805
        uint32_t const frame_id = toID3v2TagId_ucs2(fieldvalue);
Packit 47f805
        if (local_ucs2_strlen(fieldvalue) < (5+dx) || fieldvalue[4+dx] != separator) {
Packit 47f805
            return -1;
Packit 47f805
        }
Packit 47f805
        fid[0] = (frame_id >> 24) & 0x0ff;
Packit 47f805
        fid[1] = (frame_id >> 16) & 0x0ff;
Packit 47f805
        fid[2] = (frame_id >> 8) & 0x0ff;
Packit 47f805
        fid[3] = frame_id & 0x0ff;
Packit 47f805
        if (frame_id != 0) {
Packit 47f805
            unsigned short* txt = 0;
Packit 47f805
            int     rc;
Packit 47f805
            local_ucs2_substr(&txt, fieldvalue, dx+5, local_ucs2_strlen(fieldvalue));
Packit 47f805
            rc = id3tag_set_textinfo_utf16(gfp, fid, txt);
Packit 47f805
            free(txt);
Packit 47f805
            return rc;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return -1;
Packit 47f805
}
Packit 47f805
Packit 47f805
extern int
Packit 47f805
id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue);
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue)
Packit 47f805
{
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    return id3tag_set_fieldvalue_utf16(gfp, fieldvalue);
Packit 47f805
}
Packit 47f805
Packit 47f805
size_t
Packit 47f805
lame_get_id3v2_tag(lame_t gfp, unsigned char *buffer, size_t size)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = 0;
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
    if (test_tag_spec_flags(gfc, V1_ONLY_FLAG)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
#if 0
Packit 47f805
    debug_tag_spec_flags(gfc, "lame_get_id3v2_tag");
Packit 47f805
#endif
Packit 47f805
    {
Packit 47f805
        int usev2 = test_tag_spec_flags(gfc, ADD_V2_FLAG | V2_ONLY_FLAG);
Packit 47f805
        /* calculate length of four fields which may not fit in verion 1 tag */
Packit 47f805
        size_t  title_length = gfc->tag_spec.title ? strlen(gfc->tag_spec.title) : 0;
Packit 47f805
        size_t  artist_length = gfc->tag_spec.artist ? strlen(gfc->tag_spec.artist) : 0;
Packit 47f805
        size_t  album_length = gfc->tag_spec.album ? strlen(gfc->tag_spec.album) : 0;
Packit 47f805
        size_t  comment_length = gfc->tag_spec.comment ? strlen(gfc->tag_spec.comment) : 0;
Packit 47f805
        /* write tag if explicitly requested or if fields overflow */
Packit 47f805
        if ((title_length > 30)
Packit 47f805
            || (artist_length > 30)
Packit 47f805
            || (album_length > 30)
Packit 47f805
            || (comment_length > 30)
Packit 47f805
            || (gfc->tag_spec.track_id3v1 && (comment_length > 28))) {
Packit 47f805
            usev2 = 1;
Packit 47f805
        }
Packit 47f805
        if (usev2) {
Packit 47f805
            size_t  tag_size;
Packit 47f805
            unsigned char *p;
Packit 47f805
            size_t  adjusted_tag_size;
Packit 47f805
            const char *albumart_mime = NULL;
Packit 47f805
            static const char *mime_jpeg = "image/jpeg";
Packit 47f805
            static const char *mime_png = "image/png";
Packit 47f805
            static const char *mime_gif = "image/gif";
Packit 47f805
Packit 47f805
            if (gfp->num_samples != MAX_U_32_NUM) {
Packit 47f805
                id3v2AddAudioDuration(gfp, gfp->num_samples);
Packit 47f805
            }
Packit 47f805
Packit 47f805
            /* calulate size of tag starting with 10-byte tag header */
Packit 47f805
            tag_size = 10;
Packit 47f805
            if (gfc->tag_spec.albumart && gfc->tag_spec.albumart_size) {
Packit 47f805
                switch (gfc->tag_spec.albumart_mimetype) {
Packit 47f805
                case MIMETYPE_JPEG:
Packit 47f805
                    albumart_mime = mime_jpeg;
Packit 47f805
                    break;
Packit 47f805
                case MIMETYPE_PNG:
Packit 47f805
                    albumart_mime = mime_png;
Packit 47f805
                    break;
Packit 47f805
                case MIMETYPE_GIF:
Packit 47f805
                    albumart_mime = mime_gif;
Packit 47f805
                    break;
Packit 47f805
                }
Packit 47f805
                if (albumart_mime) {
Packit 47f805
                    tag_size += 10 + 4 + strlen(albumart_mime) + gfc->tag_spec.albumart_size;
Packit 47f805
                }
Packit 47f805
            }
Packit 47f805
            {
Packit 47f805
                id3tag_spec *tag = &gfc->tag_spec;
Packit 47f805
                if (tag->v2_head != 0) {
Packit 47f805
                    FrameDataNode *node;
Packit 47f805
                    for (node = tag->v2_head; node != 0; node = node->nxt) {
Packit 47f805
                        if (node->fid == ID_COMMENT || node->fid == ID_USER) {
Packit 47f805
                            tag_size += sizeOfCommentNode(node);
Packit 47f805
                        }
Packit 47f805
                        else if (isFrameIdMatching(node->fid, FRAME_ID('W',0,0,0))) {
Packit 47f805
                            tag_size += sizeOfWxxxNode(node);
Packit 47f805
                        }
Packit 47f805
                        else {
Packit 47f805
                            tag_size += sizeOfNode(node);
Packit 47f805
                        }
Packit 47f805
                    }
Packit 47f805
                }
Packit 47f805
            }
Packit 47f805
            if (test_tag_spec_flags(gfc, PAD_V2_FLAG)) {
Packit 47f805
                /* add some bytes of padding */
Packit 47f805
                tag_size += gfc->tag_spec.padding_size;
Packit 47f805
            }
Packit 47f805
            if (size < tag_size) {
Packit 47f805
                return tag_size;
Packit 47f805
            }
Packit 47f805
            if (buffer == 0) {
Packit 47f805
                return 0;
Packit 47f805
            }
Packit 47f805
            p = buffer;
Packit 47f805
            /* set tag header starting with file identifier */
Packit 47f805
            *p++ = 'I';
Packit 47f805
            *p++ = 'D';
Packit 47f805
            *p++ = '3';
Packit 47f805
            /* set version number word */
Packit 47f805
            *p++ = 3;
Packit 47f805
            *p++ = 0;
Packit 47f805
            /* clear flags byte */
Packit 47f805
            *p++ = 0;
Packit 47f805
            /* calculate and set tag size = total size - header size */
Packit 47f805
            adjusted_tag_size = tag_size - 10;
Packit 47f805
            /* encode adjusted size into four bytes where most significant 
Packit 47f805
             * bit is clear in each byte, for 28-bit total */
Packit 47f805
            *p++ = (unsigned char) ((adjusted_tag_size >> 21) & 0x7fu);
Packit 47f805
            *p++ = (unsigned char) ((adjusted_tag_size >> 14) & 0x7fu);
Packit 47f805
            *p++ = (unsigned char) ((adjusted_tag_size >> 7) & 0x7fu);
Packit 47f805
            *p++ = (unsigned char) (adjusted_tag_size & 0x7fu);
Packit 47f805
Packit 47f805
            /*
Packit 47f805
             * NOTE: The remainder of the tag (frames and padding, if any)
Packit 47f805
             * are not "unsynchronized" to prevent false MPEG audio headers
Packit 47f805
             * from appearing in the bitstream.  Why?  Well, most players
Packit 47f805
             * and utilities know how to skip the ID3 version 2 tag by now
Packit 47f805
             * even if they don't read its contents, and it's actually
Packit 47f805
             * very unlikely that such a false "sync" pattern would occur
Packit 47f805
             * in just the simple text frames added here.
Packit 47f805
             */
Packit 47f805
Packit 47f805
            /* set each frame in tag */
Packit 47f805
            {
Packit 47f805
                id3tag_spec *tag = &gfc->tag_spec;
Packit 47f805
                if (tag->v2_head != 0) {
Packit 47f805
                    FrameDataNode *node;
Packit 47f805
                    for (node = tag->v2_head; node != 0; node = node->nxt) {
Packit 47f805
                        if (node->fid == ID_COMMENT || node->fid == ID_USER) {
Packit 47f805
                            p = set_frame_comment(p, node);
Packit 47f805
                        }
Packit 47f805
                        else if (isFrameIdMatching(node->fid,FRAME_ID('W',0,0,0))) {
Packit 47f805
                            p = set_frame_wxxx(p, node);
Packit 47f805
                        }
Packit 47f805
                        else {
Packit 47f805
                            p = set_frame_custom2(p, node);
Packit 47f805
                        }
Packit 47f805
                    }
Packit 47f805
                }
Packit 47f805
            }
Packit 47f805
            if (albumart_mime) {
Packit 47f805
                p = set_frame_apic(p, albumart_mime, gfc->tag_spec.albumart,
Packit 47f805
                                   gfc->tag_spec.albumart_size);
Packit 47f805
            }
Packit 47f805
            /* clear any padding bytes */
Packit 47f805
            memset(p, 0, tag_size - (p - buffer));
Packit 47f805
            return tag_size;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_write_v2(lame_t gfp)
Packit 47f805
{
Packit 47f805
    lame_internal_flags *gfc = 0;
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
#if 0
Packit 47f805
    debug_tag_spec_flags(gfc, "write v2");
Packit 47f805
#endif
Packit 47f805
    if (test_tag_spec_flags(gfc, V1_ONLY_FLAG)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (test_tag_spec_flags(gfc, CHANGED_FLAG)) {
Packit 47f805
        unsigned char *tag = 0;
Packit 47f805
        size_t  tag_size, n;
Packit 47f805
Packit 47f805
        n = lame_get_id3v2_tag(gfp, 0, 0);
Packit 47f805
        tag = lame_calloc(unsigned char, n);
Packit 47f805
        if (tag == 0) {
Packit 47f805
            return -1;
Packit 47f805
        }
Packit 47f805
        tag_size = lame_get_id3v2_tag(gfp, tag, n);
Packit 47f805
        if (tag_size > n) {
Packit 47f805
            free(tag);
Packit 47f805
            return -1;
Packit 47f805
        }
Packit 47f805
        else {
Packit 47f805
            size_t  i;
Packit 47f805
            /* write tag directly into bitstream at current position */
Packit 47f805
            for (i = 0; i < tag_size; ++i) {
Packit 47f805
                add_dummy_byte(gfc, tag[i], 1);
Packit 47f805
            }
Packit 47f805
        }
Packit 47f805
        free(tag);
Packit 47f805
        return (int) tag_size; /* ok, tag should not exceed 2GB */
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
static unsigned char *
Packit 47f805
set_text_field(unsigned char *field, const char *text, size_t size, int pad)
Packit 47f805
{
Packit 47f805
    while (size--) {
Packit 47f805
        if (text && *text) {
Packit 47f805
            *field++ = *text++;
Packit 47f805
        }
Packit 47f805
        else {
Packit 47f805
            *field++ = pad;
Packit 47f805
        }
Packit 47f805
    }
Packit 47f805
    return field;
Packit 47f805
}
Packit 47f805
Packit 47f805
size_t
Packit 47f805
lame_get_id3v1_tag(lame_t gfp, unsigned char *buffer, size_t size)
Packit 47f805
{
Packit 47f805
    size_t const tag_size = 128;
Packit 47f805
    lame_internal_flags *gfc;
Packit 47f805
Packit 47f805
    if (gfp == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (size < tag_size) {
Packit 47f805
        return tag_size;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
    if (gfc == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (buffer == 0) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (test_tag_spec_flags(gfc, V2_ONLY_FLAG)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    if (test_tag_spec_flags(gfc, CHANGED_FLAG)) {
Packit 47f805
        unsigned char *p = buffer;
Packit 47f805
        int     pad = test_tag_spec_flags(gfc, SPACE_V1_FLAG) ? ' ' : 0;
Packit 47f805
        char    year[5];
Packit 47f805
Packit 47f805
        /* set tag identifier */
Packit 47f805
        *p++ = 'T';
Packit 47f805
        *p++ = 'A';
Packit 47f805
        *p++ = 'G';
Packit 47f805
        /* set each field in tag */
Packit 47f805
        p = set_text_field(p, gfc->tag_spec.title, 30, pad);
Packit 47f805
        p = set_text_field(p, gfc->tag_spec.artist, 30, pad);
Packit 47f805
        p = set_text_field(p, gfc->tag_spec.album, 30, pad);
Packit 47f805
        sprintf(year, "%d", gfc->tag_spec.year);
Packit 47f805
        p = set_text_field(p, gfc->tag_spec.year ? year : NULL, 4, pad);
Packit 47f805
        /* limit comment field to 28 bytes if a track is specified */
Packit 47f805
        p = set_text_field(p, gfc->tag_spec.comment, gfc->tag_spec.track_id3v1 ? 28 : 30, pad);
Packit 47f805
        if (gfc->tag_spec.track_id3v1) {
Packit 47f805
            /* clear the next byte to indicate a version 1.1 tag */
Packit 47f805
            *p++ = 0;
Packit 47f805
            *p++ = gfc->tag_spec.track_id3v1;
Packit 47f805
        }
Packit 47f805
        *p++ = gfc->tag_spec.genre_id3v1;
Packit 47f805
        return tag_size;
Packit 47f805
    }
Packit 47f805
    return 0;
Packit 47f805
}
Packit 47f805
Packit 47f805
int
Packit 47f805
id3tag_write_v1(lame_t gfp)
Packit 47f805
{
Packit 47f805
    lame_internal_flags* gfc = 0;
Packit 47f805
    size_t  i, n, m;
Packit 47f805
    unsigned char tag[128];
Packit 47f805
Packit 47f805
    if (is_lame_internal_flags_null(gfp)) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    gfc = gfp->internal_flags;
Packit 47f805
Packit 47f805
    m = sizeof(tag);
Packit 47f805
    n = lame_get_id3v1_tag(gfp, tag, m);
Packit 47f805
    if (n > m) {
Packit 47f805
        return 0;
Packit 47f805
    }
Packit 47f805
    /* write tag directly into bitstream at current position */
Packit 47f805
    for (i = 0; i < n; ++i) {
Packit 47f805
        add_dummy_byte(gfc, tag[i], 1);
Packit 47f805
    }
Packit 47f805
    return (int) n;     /* ok, tag has fixed size of 128 bytes, well below 2GB */
Packit 47f805
}