1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-05 16:43:52 +01:00
Files
audacity/lib-src/libsndfile/src/ogg_vcomment.c
Leland Lucius b749a16943 Update libsndfile to 1.0.29pre2+git
This pulls in MANY (over 890) changes compared to our
from our current 1.0.24 version.
2020-03-16 22:41:09 -05:00

278 lines
7.3 KiB
C

/*
** Copyright (C) 2008-2019 Erik de Castro Lopo <erikd@mega-nerd.com>
** Copyright (C) 2018 Arthur Taylor <art@ified.ca>
**
** This program is free software ; you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation ; either version 2.1 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY ; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with this program ; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sfconfig.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "sndfile.h"
#include "sfendian.h"
#include "common.h"
#if HAVE_EXTERNAL_XIPH_LIBS
#include <ogg/ogg.h>
#include "ogg_vcomment.h"
typedef struct
{ int id ;
const char *name ;
} STR_PAIR ;
/* See https://xiph.org/vorbis/doc/v-comment.html */
static STR_PAIR vorbiscomment_mapping [] =
{ { SF_STR_TITLE, "TITLE" },
{ SF_STR_COPYRIGHT, "COPYRIGHT", },
{ SF_STR_SOFTWARE, "ENCODER", },
{ SF_STR_ARTIST, "ARTIST" },
{ SF_STR_COMMENT, "COMMENT" },
{ SF_STR_DATE, "DATE", },
{ SF_STR_ALBUM, "ALBUM" },
{ SF_STR_LICENSE, "LICENSE", },
{ SF_STR_TRACKNUMBER, "TRACKNUMBER", },
{ SF_STR_GENRE, "GENRE", },
{ 0, NULL, },
} ;
/*-----------------------------------------------------------------------------------------------
** Private function prototypes.
*/
static int vorbiscomment_lookup_id (const char *name) ;
static const char * vorbiscomment_lookup_name (int id) ;
static inline size_t read_32bit_size_t (const unsigned char * ptr)
{ /* Read a 32 bit positive value from the provided pointer. */
return LE2H_32_PTR (ptr) & 0x7fffffff ;
} /* read_32bit_size_t */
/*-----------------------------------------------------------------------------------------------
** Exported functions.
*/
int
vorbiscomment_read_tags (SF_PRIVATE *psf, ogg_packet *packet, vorbiscomment_ident *ident)
{ unsigned char *p, *ep ;
char *tag, *c ;
size_t tag_size, tag_len = 0 ;
unsigned int ntags, i = 0 ;
int id, ret = 0 ;
/*
** The smallest possible header is the ident string length plus two 4-byte
** integers, (vender string length, tags count.)
*/
if (packet->bytes < (ident ? ident->length : 0) + 4 + 4)
return SFE_MALFORMED_FILE ;
/* Our working pointer. */
p = packet->packet ;
/* Our end pointer for bound checking. */
ep = p + packet->bytes ;
if (ident)
{ if (memcmp (p, ident->ident, ident->length) != 0)
{ psf_log_printf (psf, "Expected comment packet identifier missing.\n") ;
return SFE_MALFORMED_FILE ;
} ;
p += ident->length ;
} ;
tag_size = 1024 ;
tag = malloc (tag_size) ;
/* Unlikely */
if (!tag)
return SFE_MALLOC_FAILED ;
psf_log_printf (psf, "VorbisComment Metadata\n") ;
/*
** Vendor tag, manditory, no field name.
*/
tag_len = read_32bit_size_t (p) ;
p += 4 ;
if (tag_len > 0)
{ /* Bound checking. 4 bytes for remaining manditory fields. */
if (p + tag_len + 4 > ep)
{ ret = SFE_MALFORMED_FILE ;
goto free_tag_out ;
} ;
if (tag_len > tag_size - 1)
{ free (tag) ;
tag_size = tag_len + 1 ;
tag = malloc (tag_size) ;
/* Unlikely */
if (!tag)
return SFE_MALLOC_FAILED ;
} ;
memcpy (tag, p, tag_len) ; p += tag_len ;
tag [tag_len] = '\0' ;
psf_log_printf (psf, " Vendor: %s\n", tag) ;
} ;
/*
** List of tags of the form NAME=value
** Allowable characters for NAME are the same as shell variable names.
*/
ntags = read_32bit_size_t (p) ;
p += 4 ;
for (i = 0 ; i < ntags ; i++)
{ if (p + 4 > ep)
{ ret = SFE_MALFORMED_FILE ;
goto free_tag_out ;
} ;
tag_len = read_32bit_size_t (p) ;
p += 4 ;
if (p + tag_len > ep)
{ ret = SFE_MALFORMED_FILE ;
goto free_tag_out ;
} ;
if (tag_len > tag_size - 1)
{ free (tag) ;
tag_size = tag_len + 1 ;
tag = malloc (tag_size) ;
/* Unlikely */
if (!tag)
return SFE_MALLOC_FAILED ;
} ;
memcpy (tag, p, tag_len) ; p += tag_len ;
tag [tag_len] = '\0' ;
psf_log_printf (psf, " %s\n", tag) ;
for (c = tag ; *c ; c++)
{ if (*c == '=')
break ;
*c = toupper (*c) ;
} ;
if (!c)
{ psf_log_printf (psf, "Malformed Vorbis comment, no '=' found.\n") ;
continue ;
} ;
*c = '\0' ;
if ((id = vorbiscomment_lookup_id (tag)) != 0)
psf_store_string (psf, id, c + 1) ;
} ;
free_tag_out:
if (tag != NULL)
free (tag) ;
return ret ;
} /* vorbiscomment_read_tags */
int
vorbiscomment_write_tags (SF_PRIVATE *psf, ogg_packet *packet, vorbiscomment_ident *ident, const char *vendor, int targetsize)
{ int i, ntags ;
int tags_start ;
const char *tag_name ;
int tag_name_len, tag_body_len ;
psf->header.ptr [0] = 0 ;
psf->header.indx = 0 ;
/* Packet identifier */
if (ident)
psf_binheader_writef (psf, "eb", BHWv (ident->ident), BHWz (ident->length)) ;
/* Manditory Vendor Tag */
tag_name_len = vendor ? strlen (vendor) : 0 ;
psf_binheader_writef (psf, "e4b", BHW4 (tag_name_len), BHWv (vendor), BHWz (tag_name_len)) ;
/* Tags Count. Skip for now, write after. */
tags_start = psf->header.indx ;
psf_binheader_writef (psf, "j", BHWj (4)) ;
ntags = 0 ;
/* Write each tag */
for (i = 0 ; i < SF_MAX_STRINGS ; i++)
{ if (psf->strings.data [i].type == 0)
continue ;
tag_name = vorbiscomment_lookup_name (psf->strings.data [i].type) ;
if (tag_name == NULL)
continue ;
tag_name_len = strlen (tag_name) ;
tag_body_len = strlen (psf->strings.storage + psf->strings.data [i].offset) ;
if (targetsize > 0 && tag_name_len + tag_body_len + psf->header.indx > targetsize)
{ /* If we are out of space, stop now. */
return SFE_STR_MAX_DATA ;
}
psf_binheader_writef (psf, "e4b1b",
BHW4 (tag_name_len + 1 + tag_body_len),
BHWv (tag_name), BHWz (tag_name_len),
BHW1 ('='),
BHWv (psf->strings.storage + psf->strings.data [i].offset), BHWz (tag_body_len)) ;
ntags++ ;
} ;
if (targetsize < 0)
{ /*
** Padding.
**
** Pad to a minimum of -targetsize, but make sure length % 255
** = 254 so that we get the most out of the ogg segment lacing.
*/
psf_binheader_writef (psf, "z", BHWz ((psf->header.indx + -targetsize + 255) / 255 * 255 - 1)) ;
}
else if (targetsize > 0)
psf_binheader_writef (psf, "z", BHWz (targetsize - psf->header.indx)) ;
packet->packet = psf->header.ptr ;
packet->bytes = psf->header.indx ;
packet->b_o_s = 0 ;
packet->e_o_s = 0 ;
/* Seek back and write the tag count. */
psf_binheader_writef (psf, "eo4", BHWo (tags_start), BHW4 (ntags)) ;
return 0 ;
} /* vorbiscomment_write_tags */
/*==============================================================================
** Private functions.
*/
static int
vorbiscomment_lookup_id (const char * name)
{ STR_PAIR *p ;
for (p = vorbiscomment_mapping ; p->id ; p++)
{ if (!strcmp (name, p->name))
return p->id ;
} ;
return 0 ;
} /* vorbiscomment_lookup_id */
static const char *
vorbiscomment_lookup_name (int id)
{ STR_PAIR *p ;
for (p = vorbiscomment_mapping ; p->id ; p++)
{ if (p->id == id)
return p->name ;
} ;
return NULL ;
} /* vorbiscomment_lookup_name */
#endif /* HAVE_EXTERNAL_XIPH_LIBS */