mirror of
https://github.com/cookiengineer/audacity
synced 2025-11-05 16:43:52 +01:00
278 lines
7.3 KiB
C
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 */
|