diff options
Diffstat (limited to 'thirdparty/opus/info.c')
| -rw-r--r-- | thirdparty/opus/info.c | 753 | 
1 files changed, 753 insertions, 0 deletions
diff --git a/thirdparty/opus/info.c b/thirdparty/opus/info.c new file mode 100644 index 0000000000..55e2906d5f --- /dev/null +++ b/thirdparty/opus/info.c @@ -0,0 +1,753 @@ +/******************************************************************** + *                                                                  * + * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * + *                                                                  * + * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012                * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + *                                                                  * + ********************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "internal.h" +#include <limits.h> +#include <string.h> + +static unsigned op_parse_uint16le(const unsigned char *_data){ +  return _data[0]|_data[1]<<8; +} + +static int op_parse_int16le(const unsigned char *_data){ +  int ret; +  ret=_data[0]|_data[1]<<8; +  return (ret^0x8000)-0x8000; +} + +static opus_uint32 op_parse_uint32le(const unsigned char *_data){ +  return _data[0]|(opus_uint32)_data[1]<<8| +   (opus_uint32)_data[2]<<16|(opus_uint32)_data[3]<<24; +} + +static opus_uint32 op_parse_uint32be(const unsigned char *_data){ +  return _data[3]|(opus_uint32)_data[2]<<8| +   (opus_uint32)_data[1]<<16|(opus_uint32)_data[0]<<24; +} + +int opus_head_parse(OpusHead *_head,const unsigned char *_data,size_t _len){ +  OpusHead head; +  if(_len<8)return OP_ENOTFORMAT; +  if(memcmp(_data,"OpusHead",8)!=0)return OP_ENOTFORMAT; +  if(_len<9)return OP_EBADHEADER; +  head.version=_data[8]; +  if(head.version>15)return OP_EVERSION; +  if(_len<19)return OP_EBADHEADER; +  head.channel_count=_data[9]; +  head.pre_skip=op_parse_uint16le(_data+10); +  head.input_sample_rate=op_parse_uint32le(_data+12); +  head.output_gain=op_parse_int16le(_data+16); +  head.mapping_family=_data[18]; +  if(head.mapping_family==0){ +    if(head.channel_count<1||head.channel_count>2)return OP_EBADHEADER; +    if(head.version<=1&&_len>19)return OP_EBADHEADER; +    head.stream_count=1; +    head.coupled_count=head.channel_count-1; +    if(_head!=NULL){ +      _head->mapping[0]=0; +      _head->mapping[1]=1; +    } +  } +  else if(head.mapping_family==1){ +    size_t size; +    int    ci; +    if(head.channel_count<1||head.channel_count>8)return OP_EBADHEADER; +    size=21+head.channel_count; +    if(_len<size||head.version<=1&&_len>size)return OP_EBADHEADER; +    head.stream_count=_data[19]; +    if(head.stream_count<1)return OP_EBADHEADER; +    head.coupled_count=_data[20]; +    if(head.coupled_count>head.stream_count)return OP_EBADHEADER; +    for(ci=0;ci<head.channel_count;ci++){ +      if(_data[21+ci]>=head.stream_count+head.coupled_count +       &&_data[21+ci]!=255){ +        return OP_EBADHEADER; +      } +    } +    if(_head!=NULL)memcpy(_head->mapping,_data+21,head.channel_count); +  } +  /*General purpose players should not attempt to play back content with +     channel mapping family 255.*/ +  else if(head.mapping_family==255)return OP_EIMPL; +  /*No other channel mapping families are currently defined.*/ +  else return OP_EBADHEADER; +  if(_head!=NULL)memcpy(_head,&head,head.mapping-(unsigned char *)&head); +  return 0; +} + +void opus_tags_init(OpusTags *_tags){ +  memset(_tags,0,sizeof(*_tags)); +} + +void opus_tags_clear(OpusTags *_tags){ +  int ncomments; +  int ci; +  ncomments=_tags->comments; +  if(_tags->user_comments!=NULL)ncomments++; +  for(ci=ncomments;ci-->0;)_ogg_free(_tags->user_comments[ci]); +  _ogg_free(_tags->user_comments); +  _ogg_free(_tags->comment_lengths); +  _ogg_free(_tags->vendor); +} + +/*Ensure there's room for up to _ncomments comments.*/ +static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){ +  char   **user_comments; +  int     *comment_lengths; +  int      cur_ncomments; +  char    *binary_suffix_data; +  int      binary_suffix_len; +  size_t   size; +  if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT; +  size=sizeof(*_tags->comment_lengths)*(_ncomments+1); +  if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT; +  cur_ncomments=_tags->comments; +  comment_lengths=_tags->comment_lengths; +  binary_suffix_len=comment_lengths==NULL?0:comment_lengths[cur_ncomments]; +  comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size); +  if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT; +  comment_lengths[_ncomments]=binary_suffix_len; +  _tags->comment_lengths=comment_lengths; +  size=sizeof(*_tags->user_comments)*(_ncomments+1); +  if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT; +  user_comments=_tags->user_comments; +  binary_suffix_data=user_comments==NULL?NULL:user_comments[cur_ncomments]; +  user_comments=(char **)_ogg_realloc(_tags->user_comments,size); +  if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT; +  user_comments[_ncomments]=binary_suffix_data; +  _tags->user_comments=user_comments; +  return 0; +} + +/*Duplicate a (possibly non-NUL terminated) string with a known length.*/ +static char *op_strdup_with_len(const char *_s,size_t _len){ +  size_t  size; +  char   *ret; +  size=sizeof(*ret)*(_len+1); +  if(OP_UNLIKELY(size<_len))return NULL; +  ret=(char *)_ogg_malloc(size); +  if(OP_LIKELY(ret!=NULL)){ +    ret=(char *)memcpy(ret,_s,sizeof(*ret)*_len); +    ret[_len]='\0'; +  } +  return ret; +} + +/*The actual implementation of opus_tags_parse(). +  Unlike the public API, this function requires _tags to already be +   initialized, modifies its contents before success is guaranteed, and assumes +   the caller will clear it on error.*/ +static int opus_tags_parse_impl(OpusTags *_tags, + const unsigned char *_data,size_t _len){ +  opus_uint32 count; +  size_t      len; +  int         ncomments; +  int         ci; +  len=_len; +  if(len<8)return OP_ENOTFORMAT; +  if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT; +  if(len<16)return OP_EBADHEADER; +  _data+=8; +  len-=8; +  count=op_parse_uint32le(_data); +  _data+=4; +  len-=4; +  if(count>len)return OP_EBADHEADER; +  if(_tags!=NULL){ +    _tags->vendor=op_strdup_with_len((char *)_data,count); +    if(_tags->vendor==NULL)return OP_EFAULT; +  } +  _data+=count; +  len-=count; +  if(len<4)return OP_EBADHEADER; +  count=op_parse_uint32le(_data); +  _data+=4; +  len-=4; +  /*Check to make sure there's minimally sufficient data left in the packet.*/ +  if(count>len>>2)return OP_EBADHEADER; +  /*Check for overflow (the API limits this to an int).*/ +  if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT; +  if(_tags!=NULL){ +    int ret; +    ret=op_tags_ensure_capacity(_tags,count); +    if(ret<0)return ret; +  } +  ncomments=(int)count; +  for(ci=0;ci<ncomments;ci++){ +    /*Check to make sure there's minimally sufficient data left in the packet.*/ +    if((size_t)(ncomments-ci)>len>>2)return OP_EBADHEADER; +    count=op_parse_uint32le(_data); +    _data+=4; +    len-=4; +    if(count>len)return OP_EBADHEADER; +    /*Check for overflow (the API limits this to an int).*/ +    if(count>(opus_uint32)INT_MAX)return OP_EFAULT; +    if(_tags!=NULL){ +      _tags->user_comments[ci]=op_strdup_with_len((char *)_data,count); +      if(_tags->user_comments[ci]==NULL)return OP_EFAULT; +      _tags->comment_lengths[ci]=(int)count; +      _tags->comments=ci+1; +    } +    _data+=count; +    len-=count; +  } +  if(len>0&&(_data[0]&1)){ +    if(len>(opus_uint32)INT_MAX)return OP_EFAULT; +    _tags->user_comments[ncomments]=(char *)_ogg_malloc(len); +    if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT; +    memcpy(_tags->user_comments[ncomments],_data,len); +    _tags->comment_lengths[ncomments]=(int)len; +  } +  return 0; +} + +int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){ +  if(_tags!=NULL){ +    OpusTags tags; +    int      ret; +    opus_tags_init(&tags); +    ret=opus_tags_parse_impl(&tags,_data,_len); +    if(ret<0)opus_tags_clear(&tags); +    else *_tags=*&tags; +    return ret; +  } +  else return opus_tags_parse_impl(NULL,_data,_len); +} + +/*The actual implementation of opus_tags_copy(). +  Unlike the public API, this function requires _dst to already be +   initialized, modifies its contents before success is guaranteed, and assumes +   the caller will clear it on error.*/ +static int opus_tags_copy_impl(OpusTags *_dst,const OpusTags *_src){ +  char *vendor; +  int   ncomments; +  int   ret; +  int   ci; +  vendor=_src->vendor; +  _dst->vendor=op_strdup_with_len(vendor,strlen(vendor)); +  if(OP_UNLIKELY(_dst->vendor==NULL))return OP_EFAULT; +  ncomments=_src->comments; +  ret=op_tags_ensure_capacity(_dst,ncomments); +  if(OP_UNLIKELY(ret<0))return ret; +  for(ci=0;ci<ncomments;ci++){ +    int len; +    len=_src->comment_lengths[ci]; +    OP_ASSERT(len>=0); +    _dst->user_comments[ci]=op_strdup_with_len(_src->user_comments[ci],len); +    if(OP_UNLIKELY(_dst->user_comments[ci]==NULL))return OP_EFAULT; +    _dst->comment_lengths[ci]=len; +    _dst->comments=ci+1; +  } +  if(_src->comment_lengths!=NULL){ +    int len; +    len=_src->comment_lengths[ncomments]; +    if(len>0){ +      _dst->user_comments[ncomments]=(char *)_ogg_malloc(len); +      if(OP_UNLIKELY(_dst->user_comments[ncomments]==NULL))return OP_EFAULT; +      memcpy(_dst->user_comments[ncomments],_src->user_comments[ncomments],len); +      _dst->comment_lengths[ncomments]=len; +    } +  } +  return 0; +} + +int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){ +  OpusTags dst; +  int      ret; +  opus_tags_init(&dst); +  ret=opus_tags_copy_impl(&dst,_src); +  if(OP_UNLIKELY(ret<0))opus_tags_clear(&dst); +  else *_dst=*&dst; +  return 0; +} + +int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){ +  char *comment; +  int   tag_len; +  int   value_len; +  int   ncomments; +  int   ret; +  ncomments=_tags->comments; +  ret=op_tags_ensure_capacity(_tags,ncomments+1); +  if(OP_UNLIKELY(ret<0))return ret; +  tag_len=strlen(_tag); +  value_len=strlen(_value); +  /*+2 for '=' and '\0'.*/ +  comment=(char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2)); +  if(OP_UNLIKELY(comment==NULL))return OP_EFAULT; +  memcpy(comment,_tag,sizeof(*comment)*tag_len); +  comment[tag_len]='='; +  memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1)); +  _tags->user_comments[ncomments]=comment; +  _tags->comment_lengths[ncomments]=tag_len+value_len+1; +  _tags->comments=ncomments+1; +  return 0; +} + +int opus_tags_add_comment(OpusTags *_tags,const char *_comment){ +  char *comment; +  int   comment_len; +  int   ncomments; +  int   ret; +  ncomments=_tags->comments; +  ret=op_tags_ensure_capacity(_tags,ncomments+1); +  if(OP_UNLIKELY(ret<0))return ret; +  comment_len=(int)strlen(_comment); +  comment=op_strdup_with_len(_comment,comment_len); +  if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT; +  _tags->user_comments[ncomments]=comment; +  _tags->comment_lengths[ncomments]=comment_len; +  _tags->comments=ncomments+1; +  return 0; +} + +int opus_tags_set_binary_suffix(OpusTags *_tags, + const unsigned char *_data,int _len){ +  unsigned char *binary_suffix_data; +  int            ncomments; +  int            ret; +  if(_len<0||_len>0&&(_data==NULL||!(_data[0]&1)))return OP_EINVAL; +  ncomments=_tags->comments; +  ret=op_tags_ensure_capacity(_tags,ncomments); +  if(OP_UNLIKELY(ret<0))return ret; +  binary_suffix_data= +   (unsigned char *)_ogg_realloc(_tags->user_comments[ncomments],_len); +  if(OP_UNLIKELY(binary_suffix_data==NULL))return OP_EFAULT; +  memcpy(binary_suffix_data,_data,_len); +  _tags->user_comments[ncomments]=(char *)binary_suffix_data; +  _tags->comment_lengths[ncomments]=_len; +  return 0; +} + +int opus_tagcompare(const char *_tag_name,const char *_comment){ +  return opus_tagncompare(_tag_name,strlen(_tag_name),_comment); +} + +int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){ +  int ret; +  OP_ASSERT(_tag_len>=0); +  ret=op_strncasecmp(_tag_name,_comment,_tag_len); +  return ret?ret:'='-_comment[_tag_len]; +} + +const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){ +  char **user_comments; +  int    tag_len; +  int    found; +  int    ncomments; +  int    ci; +  tag_len=strlen(_tag); +  ncomments=_tags->comments; +  user_comments=_tags->user_comments; +  found=0; +  for(ci=0;ci<ncomments;ci++){ +    if(!opus_tagncompare(_tag,tag_len,user_comments[ci])){ +      /*We return a pointer to the data, not a copy.*/ +      if(_count==found++)return user_comments[ci]+tag_len+1; +    } +  } +  /*Didn't find anything.*/ +  return NULL; +} + +int opus_tags_query_count(const OpusTags *_tags,const char *_tag){ +  char **user_comments; +  int    tag_len; +  int    found; +  int    ncomments; +  int    ci; +  tag_len=strlen(_tag); +  ncomments=_tags->comments; +  user_comments=_tags->user_comments; +  found=0; +  for(ci=0;ci<ncomments;ci++){ +    if(!opus_tagncompare(_tag,tag_len,user_comments[ci]))found++; +  } +  return found; +} + +const unsigned char *opus_tags_get_binary_suffix(const OpusTags *_tags, + int *_len){ +  int ncomments; +  int len; +  ncomments=_tags->comments; +  len=_tags->comment_lengths==NULL?0:_tags->comment_lengths[ncomments]; +  *_len=len; +  OP_ASSERT(len==0||_tags->user_comments!=NULL); +  return len>0?(const unsigned char *)_tags->user_comments[ncomments]:NULL; +} + +static int opus_tags_get_gain(const OpusTags *_tags,int *_gain_q8, + const char *_tag_name,size_t _tag_len){ +  char **comments; +  int    ncomments; +  int    ci; +  comments=_tags->user_comments; +  ncomments=_tags->comments; +  /*Look for the first valid tag with the name _tag_name and use that.*/ +  for(ci=0;ci<ncomments;ci++){ +    if(opus_tagncompare(_tag_name,_tag_len,comments[ci])==0){ +      char       *p; +      opus_int32  gain_q8; +      int         negative; +      p=comments[ci]+_tag_len+1; +      negative=0; +      if(*p=='-'){ +        negative=-1; +        p++; +      } +      else if(*p=='+')p++; +      gain_q8=0; +      while(*p>='0'&&*p<='9'){ +        gain_q8=10*gain_q8+*p-'0'; +        if(gain_q8>32767-negative)break; +        p++; +      } +      /*This didn't look like a signed 16-bit decimal integer. +        Not a valid gain tag.*/ +      if(*p!='\0')continue; +      *_gain_q8=(int)(gain_q8+negative^negative); +      return 0; +    } +  } +  return OP_FALSE; +} + +int opus_tags_get_album_gain(const OpusTags *_tags,int *_gain_q8){ +  return opus_tags_get_gain(_tags,_gain_q8,"R128_ALBUM_GAIN",15); +} + +int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8){ +  return opus_tags_get_gain(_tags,_gain_q8,"R128_TRACK_GAIN",15); +} + +static int op_is_jpeg(const unsigned char *_buf,size_t _buf_sz){ +  return _buf_sz>=11&&memcmp(_buf,"\xFF\xD8\xFF\xE0",4)==0 +   &&(_buf[4]<<8|_buf[5])>=16&&memcmp(_buf+6,"JFIF",5)==0; +} + +/*Tries to extract the width, height, bits per pixel, and palette size of a +   JPEG. +  On failure, simply leaves its outputs unmodified.*/ +static void op_extract_jpeg_params(const unsigned char *_buf,size_t _buf_sz, + opus_uint32 *_width,opus_uint32 *_height, + opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){ +  if(op_is_jpeg(_buf,_buf_sz)){ +    size_t offs; +    offs=2; +    for(;;){ +      size_t segment_len; +      int    marker; +      while(offs<_buf_sz&&_buf[offs]!=0xFF)offs++; +      while(offs<_buf_sz&&_buf[offs]==0xFF)offs++; +      marker=_buf[offs]; +      offs++; +      /*If we hit EOI* (end of image), or another SOI* (start of image), +         or SOS (start of scan), then stop now.*/ +      if(offs>=_buf_sz||(marker>=0xD8&&marker<=0xDA))break; +      /*RST* (restart markers): skip (no segment length).*/ +      else if(marker>=0xD0&&marker<=0xD7)continue; +      /*Read the length of the marker segment.*/ +      if(_buf_sz-offs<2)break; +      segment_len=_buf[offs]<<8|_buf[offs+1]; +      if(segment_len<2||_buf_sz-offs<segment_len)break; +      if(marker==0xC0||(marker>0xC0&&marker<0xD0&&(marker&3)!=0)){ +        /*Found a SOFn (start of frame) marker segment:*/ +        if(segment_len>=8){ +          *_height=_buf[offs+3]<<8|_buf[offs+4]; +          *_width=_buf[offs+5]<<8|_buf[offs+6]; +          *_depth=_buf[offs+2]*_buf[offs+7]; +          *_colors=0; +          *_has_palette=0; +        } +        break; +      } +      /*Other markers: skip the whole marker segment.*/ +      offs+=segment_len; +    } +  } +} + +static int op_is_png(const unsigned char *_buf,size_t _buf_sz){ +  return _buf_sz>=8&&memcmp(_buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0; +} + +/*Tries to extract the width, height, bits per pixel, and palette size of a +   PNG. +  On failure, simply leaves its outputs unmodified.*/ +static void op_extract_png_params(const unsigned char *_buf,size_t _buf_sz, + opus_uint32 *_width,opus_uint32 *_height, + opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){ +  if(op_is_png(_buf,_buf_sz)){ +    size_t offs; +    offs=8; +    while(_buf_sz-offs>=12){ +      ogg_uint32_t chunk_len; +      chunk_len=op_parse_uint32be(_buf+offs); +      if(chunk_len>_buf_sz-(offs+12))break; +      else if(chunk_len==13&&memcmp(_buf+offs+4,"IHDR",4)==0){ +        int color_type; +        *_width=op_parse_uint32be(_buf+offs+8); +        *_height=op_parse_uint32be(_buf+offs+12); +        color_type=_buf[offs+17]; +        if(color_type==3){ +          *_depth=24; +          *_has_palette=1; +        } +        else{ +          int sample_depth; +          sample_depth=_buf[offs+16]; +          if(color_type==0)*_depth=sample_depth; +          else if(color_type==2)*_depth=sample_depth*3; +          else if(color_type==4)*_depth=sample_depth*2; +          else if(color_type==6)*_depth=sample_depth*4; +          *_colors=0; +          *_has_palette=0; +          break; +        } +      } +      else if(*_has_palette>0&&memcmp(_buf+offs+4,"PLTE",4)==0){ +        *_colors=chunk_len/3; +        break; +      } +      offs+=12+chunk_len; +    } +  } +} + +static int op_is_gif(const unsigned char *_buf,size_t _buf_sz){ +  return _buf_sz>=6&&(memcmp(_buf,"GIF87a",6)==0||memcmp(_buf,"GIF89a",6)==0); +} + +/*Tries to extract the width, height, bits per pixel, and palette size of a +   GIF. +  On failure, simply leaves its outputs unmodified.*/ +static void op_extract_gif_params(const unsigned char *_buf,size_t _buf_sz, + opus_uint32 *_width,opus_uint32 *_height, + opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){ +  if(op_is_gif(_buf,_buf_sz)&&_buf_sz>=14){ +    *_width=_buf[6]|_buf[7]<<8; +    *_height=_buf[8]|_buf[9]<<8; +    /*libFLAC hard-codes the depth to 24.*/ +    *_depth=24; +    *_colors=1<<((_buf[10]&7)+1); +    *_has_palette=1; +  } +} + +/*The actual implementation of opus_picture_tag_parse(). +  Unlike the public API, this function requires _pic to already be +   initialized, modifies its contents before success is guaranteed, and assumes +   the caller will clear it on error.*/ +static int opus_picture_tag_parse_impl(OpusPictureTag *_pic,const char *_tag, + unsigned char *_buf,size_t _buf_sz,size_t _base64_sz){ +  opus_int32   picture_type; +  opus_uint32  mime_type_length; +  char        *mime_type; +  opus_uint32  description_length; +  char        *description; +  opus_uint32  width; +  opus_uint32  height; +  opus_uint32  depth; +  opus_uint32  colors; +  opus_uint32  data_length; +  opus_uint32  file_width; +  opus_uint32  file_height; +  opus_uint32  file_depth; +  opus_uint32  file_colors; +  int          format; +  int          has_palette; +  int          colors_set; +  size_t       i; +  /*Decode the BASE64 data.*/ +  for(i=0;i<_base64_sz;i++){ +    opus_uint32 value; +    int         j; +    value=0; +    for(j=0;j<4;j++){ +      unsigned c; +      unsigned d; +      c=(unsigned char)_tag[4*i+j]; +      if(c=='+')d=62; +      else if(c=='/')d=63; +      else if(c>='0'&&c<='9')d=52+c-'0'; +      else if(c>='a'&&c<='z')d=26+c-'a'; +      else if(c>='A'&&c<='Z')d=c-'A'; +      else if(c=='='&&3*i+j>_buf_sz)d=0; +      else return OP_ENOTFORMAT; +      value=value<<6|d; +    } +    _buf[3*i]=(unsigned char)(value>>16); +    if(3*i+1<_buf_sz){ +      _buf[3*i+1]=(unsigned char)(value>>8); +      if(3*i+2<_buf_sz)_buf[3*i+2]=(unsigned char)value; +    } +  } +  i=0; +  picture_type=op_parse_uint32be(_buf+i); +  i+=4; +  /*Extract the MIME type.*/ +  mime_type_length=op_parse_uint32be(_buf+i); +  i+=4; +  if(mime_type_length>_buf_sz-32)return OP_ENOTFORMAT; +  mime_type=(char *)_ogg_malloc(sizeof(*_pic->mime_type)*(mime_type_length+1)); +  if(mime_type==NULL)return OP_EFAULT; +  memcpy(mime_type,_buf+i,sizeof(*mime_type)*mime_type_length); +  mime_type[mime_type_length]='\0'; +  _pic->mime_type=mime_type; +  i+=mime_type_length; +  /*Extract the description string.*/ +  description_length=op_parse_uint32be(_buf+i); +  i+=4; +  if(description_length>_buf_sz-mime_type_length-32)return OP_ENOTFORMAT; +  description= +   (char *)_ogg_malloc(sizeof(*_pic->mime_type)*(description_length+1)); +  if(description==NULL)return OP_EFAULT; +  memcpy(description,_buf+i,sizeof(*description)*description_length); +  description[description_length]='\0'; +  _pic->description=description; +  i+=description_length; +  /*Extract the remaining fields.*/ +  width=op_parse_uint32be(_buf+i); +  i+=4; +  height=op_parse_uint32be(_buf+i); +  i+=4; +  depth=op_parse_uint32be(_buf+i); +  i+=4; +  colors=op_parse_uint32be(_buf+i); +  i+=4; +  /*If one of these is set, they all must be, but colors==0 is a valid value.*/ +  colors_set=width!=0||height!=0||depth!=0||colors!=0; +  if((width==0||height==0||depth==0)&&colors_set)return OP_ENOTFORMAT; +  data_length=op_parse_uint32be(_buf+i); +  i+=4; +  if(data_length>_buf_sz-i)return OP_ENOTFORMAT; +  /*Trim extraneous data so we don't copy it below.*/ +  _buf_sz=i+data_length; +  /*Attempt to determine the image format.*/ +  format=OP_PIC_FORMAT_UNKNOWN; +  if(mime_type_length==3&&strcmp(mime_type,"-->")==0){ +    format=OP_PIC_FORMAT_URL; +    /*Picture type 1 must be a 32x32 PNG.*/ +    if(picture_type==1&&(width!=0||height!=0)&&(width!=32||height!=32)){ +      return OP_ENOTFORMAT; +    } +    /*Append a terminating NUL for the convenience of our callers.*/ +    _buf[_buf_sz++]='\0'; +  } +  else{ +    if(mime_type_length==10 +     &&op_strncasecmp(mime_type,"image/jpeg",mime_type_length)==0){ +      if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG; +    } +    else if(mime_type_length==9 +     &&op_strncasecmp(mime_type,"image/png",mime_type_length)==0){ +      if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG; +    } +    else if(mime_type_length==9 +     &&op_strncasecmp(mime_type,"image/gif",mime_type_length)==0){ +      if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF; +    } +    else if(mime_type_length==0||(mime_type_length==6 +     &&op_strncasecmp(mime_type,"image/",mime_type_length)==0)){ +      if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG; +      else if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG; +      else if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF; +    } +    file_width=file_height=file_depth=file_colors=0; +    has_palette=-1; +    switch(format){ +      case OP_PIC_FORMAT_JPEG:{ +        op_extract_jpeg_params(_buf+i,data_length, +         &file_width,&file_height,&file_depth,&file_colors,&has_palette); +      }break; +      case OP_PIC_FORMAT_PNG:{ +        op_extract_png_params(_buf+i,data_length, +         &file_width,&file_height,&file_depth,&file_colors,&has_palette); +      }break; +      case OP_PIC_FORMAT_GIF:{ +        op_extract_gif_params(_buf+i,data_length, +         &file_width,&file_height,&file_depth,&file_colors,&has_palette); +      }break; +    } +    if(has_palette>=0){ +      /*If we successfully extracted these parameters from the image, override +         any declared values.*/ +      width=file_width; +      height=file_height; +      depth=file_depth; +      colors=file_colors; +    } +    /*Picture type 1 must be a 32x32 PNG.*/ +    if(picture_type==1&&(format!=OP_PIC_FORMAT_PNG||width!=32||height!=32)){ +      return OP_ENOTFORMAT; +    } +  } +  /*Adjust _buf_sz instead of using data_length to capture the terminating NUL +     for URLs.*/ +  _buf_sz-=i; +  memmove(_buf,_buf+i,sizeof(*_buf)*_buf_sz); +  _buf=(unsigned char *)_ogg_realloc(_buf,_buf_sz); +  if(_buf_sz>0&&_buf==NULL)return OP_EFAULT; +  _pic->type=picture_type; +  _pic->width=width; +  _pic->height=height; +  _pic->depth=depth; +  _pic->colors=colors; +  _pic->data_length=data_length; +  _pic->data=_buf; +  _pic->format=format; +  return 0; +} + +int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag){ +  OpusPictureTag  pic; +  unsigned char  *buf; +  size_t          base64_sz; +  size_t          buf_sz; +  size_t          tag_length; +  int             ret; +  if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,_tag)==0)_tag+=23; +  /*Figure out how much BASE64-encoded data we have.*/ +  tag_length=strlen(_tag); +  if(tag_length&3)return OP_ENOTFORMAT; +  base64_sz=tag_length>>2; +  buf_sz=3*base64_sz; +  if(buf_sz<32)return OP_ENOTFORMAT; +  if(_tag[tag_length-1]=='=')buf_sz--; +  if(_tag[tag_length-2]=='=')buf_sz--; +  if(buf_sz<32)return OP_ENOTFORMAT; +  /*Allocate an extra byte to allow appending a terminating NUL to URL data.*/ +  buf=(unsigned char *)_ogg_malloc(sizeof(*buf)*(buf_sz+1)); +  if(buf==NULL)return OP_EFAULT; +  opus_picture_tag_init(&pic); +  ret=opus_picture_tag_parse_impl(&pic,_tag,buf,buf_sz,base64_sz); +  if(ret<0){ +    opus_picture_tag_clear(&pic); +    _ogg_free(buf); +  } +  else *_pic=*&pic; +  return ret; +} + +void opus_picture_tag_init(OpusPictureTag *_pic){ +  memset(_pic,0,sizeof(*_pic)); +} + +void opus_picture_tag_clear(OpusPictureTag *_pic){ +  _ogg_free(_pic->description); +  _ogg_free(_pic->mime_type); +  _ogg_free(_pic->data); +}  |